2019年5月12日凌晨1点37分
前两天看到一句话,大概意思是永远不要想一个人去设计某种的加密方式;emmm... 我觉得对我这种正在学习过程中的人来讲,的确是个不错的建议;所以下面的代码或者思路大家用来学习就行,“切勿当真”。
可能是自己无聊,也可能闲得慌,改写了一个比较简单的字符串加解密类,支持中英文特殊字符串加解密,支持设置有效性。
先简单介绍一下Authcode这个函数,Authcode这个函数很多人都使用,这函数来自Discuz程序,用于加密解密字符串,可以设置钥匙(key)和过期时间,在很多时候都用得着。
emmm... 大佬下面就不用看了吧... 菜鸡[脸红]
在有道云的源码里面,又被其作者重整成一个类,可直接调用;以前自己菜,只知道这个函数特别牛X,相同内容每次加密密文都不同,也不知道其原理是什么;这次查了一些资料也引入了自己的一些想法。
Authcode如果用于字符串简单加密是一个不错的选择,想着要是python3 里面有类似函数就很棒,然而查了一下并没有类似作品,索性自己再造一次轮子。
再当python3 写完加密部分之后测试发现,即便相同字符串用相同密匙加密,用php解密函数竟然无法解密???,之后发现原因是两种语言的编码存在问题。
加解密过程中两种语言均会用到chr()和ord()函数,ASCII转换前126位正常,但超过126之后,py chr()是按照返回字符的unicode十进制值,然而PHP就开始返回乱码,原因是对中文和特殊字符会自动截断;思考之后决定PHP计算也直接unicode,unicode包括所有的字符(中文);所以有了上一篇博文,PHP unicode 单字符转换。
加解密原理:
其实吧,特别简单的一个计算方法,“异或”计算,如若不懂,这里不做介绍;针对于此种加密,个人的看法是,增加破解难度的最直接的办法就是提高密匙的复杂度,重改密匙簿的计算方法。
说再多也是废话,直接上效果演示。
PHP加解密效果:
Python3加解密效果:
两者混用:
混用:相同密匙的情况下,利用py3对字符串进行加密,再用PHP进行解密,很完美,当然反过来利用php加密Py解密效果也相同。
最后留下源码:
PHP
<?php
class Mcrypt {
public static $default_key = 'a!takA:dlmcldEv,e';
/*
* 字符加解密,一次一密,可定时解密有效
*
*/
public static function encode($string, $key = '', $expiry = 0) {
$ckeyLength = 16;
$key = md5($key ? $key : self::$default_key);
$key = md5(substr($key, 0, 16).$key.md5(substr($key, 16, 16)));
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = substr(md5(microtime()), -$ckeyLength);
$cryptkey = $keya . md5($keya . $keyc);
$keyLength = mb_strlen($cryptkey);
$string = sprintf('%010d', $expiry ? $expiry + intval(time()) : 0) . substr(md5($string . $keyb), 0, 16) . $string;
$stringLength = mb_strlen($string);
$rndkey = array();
for ($i = 0; $i <= 255; $i++) {
$rndkey[$i] = self::char_unicode($cryptkey[$i % $keyLength], False);
}
$box = range(0, 255);
for ($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
$result = '';
for ($a = $j = $i = 0; $i < $stringLength; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= self::char_unicode(self::char_unicode(mb_substr($string, $i, 1, 'UTF-8'), False) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
$result = $keyc . str_replace('=', '', base64_encode($result));
$result = str_replace(array(
'+',
'/',
'='
), array(
'-',
'_',
'.'
), $result);
return $result;
}
/**
* 字符加解密,一次一密,可定时解密有效
*
*/
public static function decode($string, $key = '') {
$string = str_replace(array(
'-',
'_',
'.'
), array(
'+',
'/',
'='
), $string);
$ckeyLength = 16;
$key = md5($key ? $key : self::$default_key);
$key = md5(substr($key, 0, 16).$key.md5(substr($key, 16, 16)));
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = substr($string, 0, $ckeyLength);
$cryptkey = $keya . md5($keya . $keyc);
$keyLength = strlen($cryptkey);
$string = base64_decode(substr($string, $ckeyLength));
$stringLength = mb_strlen($string);
$rndkey = array();
for ($i = 0; $i <= 255; $i++) {
$rndkey[$i] = self::char_unicode($cryptkey[$i % $keyLength], False);
}
$box = range(0, 255);
for ($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
$result = '';
for ($a = $j = $i = 0; $i < $stringLength; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= self::char_unicode(self::char_unicode(mb_substr($string, $i, 1, 'UTF-8'), False) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
return substr($result, 26);
} else {
return '';
}
}
public static function char_unicode($str, $DECODE = True) {
$result = '';
if ($DECODE === False) {
$unicodestr = intval(base_convert(bin2hex(iconv('utf-8', 'UCS-4', $str)), 16, 10));
$result = $unicodestr;
} else {
$temp = intval($str);
$result = iconv('UCS-2BE', 'utf-8', ($temp < 256) ? chr(0) . chr($temp) : chr($temp / 256) . chr($temp % 256));
}
return $result;
}
}
Python3
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : Mcrypt.class.py
# @Author: MoMing
# @Date : 2019/4/15
# @Desc : 字符串加解密
class Mcrypt:
default_key = 'a!takA:dlmcldEv,e'
@staticmethod
def encode(string='', key='', expiry=0):
"""
:param string: 待加密字符串
:param key: 加密密匙
:param expiry: 有效期,0->永久
:return:
"""
import time
from base64 import b64encode
ckeylength = 16
if key == '':
key = Mcrypt.default_key
key = Mcrypt.__md5(key)
key = Mcrypt.__md5(key[0:16] + key + Mcrypt.__md5(key[16:]))
keya = Mcrypt.__md5(key[0:16])
keyb = Mcrypt.__md5(key[16:])
keyc = Mcrypt.__md5(str(int(time.time()) * 10000))[-ckeylength:]
cryptkey = keya + Mcrypt.__md5(keya + keyc)
keylength = len(cryptkey)
if expiry != 0:
string = str(expiry + int(time.time())) + Mcrypt.__md5(string + keyb)[0:16] + string
else:
string = '0000000000' + Mcrypt.__md5(string + keyb)[0:16] + string
stringlength = len(string)
rndkey = dict()
for i in range(256):
rndkey[i] = ord(cryptkey[i % keylength])
box = dict()
for i in range(256):
box[i] = i
j = int(0)
for i in range(256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp
result = ''
a = j = int(0)
for i in range(stringlength):
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ (box[(box[a] + box[j]) % 256]))
base64 = str(b64encode(result.encode('utf-8')), 'utf-8')
result = keyc + base64.replace('=', '')
old = ['+', '/', '=']
new = ['-', '_', '.']
return Mcrypt.__str_replace(result, old, new)
@staticmethod
def decode(string='', key=''):
"""
:param string: 加密字符串
:param key: 加密密匙
:return: 解密失败返回空字符
"""
import time
from base64 import b64decode
ckeylength = 16
new = ['+', '/', '=']
old = ['-', '_', '.']
string = Mcrypt.__str_replace(string, old, new)
if key == '':
key = Mcrypt.default_key
key = Mcrypt.__md5(key)
key = Mcrypt.__md5(key[0:16] + key + Mcrypt.__md5(key[16:]))
keya = Mcrypt.__md5(key[0:16])
keyb = Mcrypt.__md5(key[16:])
keyc = string[:ckeylength]
string = string[16:]
missing_padding = 4 - len(string) % 4
if missing_padding:
string += '=' * missing_padding
string = str(b64decode(string), 'utf-8')
cryptkey = keya + Mcrypt.__md5(keya + keyc)
keylength = len(cryptkey)
stringlength = len(string)
rndkey = dict()
for i in range(256):
rndkey[i] = ord(cryptkey[i % keylength])
box = dict()
for i in range(256):
box[i] = i
j = int(0)
for i in range(256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp
result = ''
a = j = int(0)
for i in range(stringlength):
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ (box[(box[a] + box[j]) % 256]))
try:
if (int(result[:10]) == 0 or int(result[:10]) - int(time.time()) > 0) \
and result[10:10 + ckeylength] == Mcrypt.__md5(result[10 + ckeylength:] + keyb)[0:16]:
return result[10 + ckeylength:]
else:
return ''
except ValueError:
return ''
@staticmethod
def __md5(string=str()):
"""
:param string: hash 字符串
:return: md5
"""
import hashlib
m = hashlib.md5()
m.update(string.encode('utf-8'))
return m.hexdigest()
@staticmethod
def __str_replace(string, old=[], new=[]):
"""
old 和 new 长度必须相同,新旧必须对应
:param old: 将被替换的子字符列表。
:param new: 新字符串,用于替换old子字符列表
:param string: 被替换成字符串
:return: 新字符串
"""
for i in range(len(old)):
string = string.replace(old[i], new[i])
return string
使用方法:
PHP
<?php
header('Content-type:application/json;;charset=UTF-8');
require_once __dir__ . DIRECTORY_SEPARATOR . 'Mcrypt.class.php';
$str = '123456789[密文]';
$enstr = Mcrypt::encode($str);
$destr = Mcrypt::decode($enstr);
echo '原文:'.$str.PHP_EOL;
echo '加密:'.$enstr.PHP_EOL;
echo '解密:'.$destr.PHP_EOL;
Python3
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : main.py
# @Author: MoMing
# @Date : 2019/4/14
# @Desc : Mcrypt Main
from Mcrypt import Mcrypt
string = 'ABC1234567[密文]'
enstr = Mcrypt.encode(string)
destr = Mcrypt.decode(enstr)
print('原文:', string)
print('加密:', enstr)
print('解密:', destr)