可逆加密算法
2021-01-20
不可逆信息摘要算法(也称Hash算法)主要有:MD5,SHA系列,HMAC系列。
可逆加密算法分 对称式 和 非对称式,
对称式:DES,3DES,AES系列
非对称式:RSA,ECC椭圆曲线加密相关算法
基础可逆加密算法
简单的对称可逆加密算法原理,我们可以把它理解为一道数学题:
假设A有一个数字 88,接下来A想对它加密,拿一个密钥数7来加密,比如把它相加得到密文 88+7=95
A把加密后的95传输给B,B手上有协商好的密钥数7,同时也知道解密算法,那么B只要把密文减去密钥即可得到原来数字 95-7=88
以上传输过程中,只传递了密文95,原数字和密钥数都没被传输,也就实现了加密效果。
现实过程中,我们把这个数字看成计算机中的一个存储单位,比如字节(一个字节8比特),我们只要把这个字节做一遍数学运算,就能实现加密效果。
算法实现
根据以上思路,用PHP来实现对称加解密函数:
/**
* 加密函数
* By: Malu
* @param $data
* @param $key
* @return string
*/
public function encrypt($data, $key)
{
$key = md5($key);
$x = 0;
$len = strlen($data);
$l = strlen($key);
$char = "";
$str = "";
// 循环拼接私钥md5后的字符,组装到待加密字串长度
for ($i = 0; $i < $len; $i++) {
if ($x == $l) {
$x = 0;
}
$char .= $key[$x];
$x++;
}
for ($i = 0; $i < $len; $i++) {
// ord() 函数返回字符串的首个字符的 ASCII 值。
// 给每个字符循环 加上 私钥md5后的 ASCII 与 256 求模后的值(求模是防止长度越界,比如中文字符)
// 最后把 ASCII 值转成字符
$str .= chr((ord($data[$i]) + ord($char[$i])) % 256);
}
return base64_encode($str); // 用基础的64个字符替换
}
/**
* 解密函数
* By: Malu
* @param $data
* @param $key
* @return string
*/
public function decrypt($data, $key)
{
$key = md5($key);
$x = 0;
$data = base64_decode($data);
$len = strlen($data);
$l = strlen($key);
$char = "";
$str = "";
// 循环拼接私钥md5后的字符,组装到加密字串长度一样长
for ($i = 0; $i < $len; $i++) {
if ($x == $l) {
$x = 0;
}
$char .= substr($key, $x, 1);
$x++;
}
for ($i = 0; $i < $len; $i++) {
if (ord(substr($data, $i, 1)) < ord(substr($char, $i, 1))) {
// 如果加密字串ASCII小于密文ASCII,表示长度已越界,需要补256
// 那么把 加密字串ASCII + 256 - 私钥md5后的字符串ASCII
$str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1)));
} else {
$str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1)));
}
}
return $str;
}
升级算法
以上代码在PHP下运行正常,但是在异构代码下运行呢?
比如PHP项目与Java项目加密传输,标准ASCII码共定义了128个字符,而以上算法用到了256位;
为了标准化,我把它限制到128个字符,我们来改进一下算法:
1.加密原文前做一次base64,把原文降为64个字符集。
2.把加法运算改成异或运算,这样就不用做越界处理。
下面是PHP实现的升级版可逆加密算法:
//url base64编码
public function urlsafe_b64encode($string)
{
$data = base64_encode($string);
$data = str_replace(array('+', '/', '='), array('-', '_', ''), $data);
return $data;
}
//url base64解码
public function urlsafe_b64decode($string)
{
$data = str_replace(array('-', '_'), array('+', '/'), $string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
/**
* 加密函数 V2
* By: Malu
* @param $data
* @param $key
* @return string
*/
public function encrypt_v2($data, $key)
{
$key = md5($key);
$x = 0;
$data = $this->urlsafe_b64encode($data);
$len = strlen($data);
$l = strlen($key); // 32
$char = "";
$str = "";
// 循环拼接私钥md5后的字符,组装到待加密字串长度
for ($i = 0; $i < $len; $i++) {
if ($x == $l) {
$x = 0;
}
$char .= $key[$x];
$x++;
}
for ($i = 0; $i < $len; $i++) {
// ord() 函数返回字符串的首个字符的 ASCII 值。
// 给每个字符循环 私钥md5后的ASCII 与 与原文处理后的字符做异或运算
// 最后把 ASCII 值转成字符
$str .= chr(ord($data[$i]) ^ ord($char[$i]));
}
$str = $this->urlsafe_b64encode($str); // 可以在URL安全传输
return $str;
}
/**
* 解密函数v2
* By: Malu
* @param $data
* @param $key
* @return string
*/
public function decrypt_v2($data, $key)
{
$key = md5($key);
$x = 0;
$data = $this->urlsafe_b64decode($data);
$len = strlen($data);
$l = strlen($key);
$char = "";
$str = "";
// 循环拼接私钥md5后的字符,组装到加密字串长度一样长
for ($i = 0; $i < $len; $i++) {
if ($x == $l) {
$x = 0;
}
$char .= substr($key, $x, 1);
$x++;
}
for ($i = 0; $i < $len; $i++) {
// 把加密字串ASCII 与 私钥md5后的字符串ASCII 做异或运算
// 最后把 ASCII 值还原成字符
$str .= chr(ord(substr($data, $i, 1)) ^ ord(substr($char, $i, 1)));
}
$str = $this->urlsafe_b64decode($str);
return $str;
}
下面是Java实现的升级版可逆加密算法:
// ord() 函数返回字符串的首个字符的 ASCII 值
public static int ord(String s) {
return s.length() > 0 ? (s.getBytes(StandardCharsets.UTF_8)[0] & 0xff) : 0;
}
/**
* 加密函数 V2
* By: Malu
*
* @param data
* @param key
* @return
*/
public String encrypt(String data, String key) {
key = md5(key);
final Base64.Encoder encoder = Base64.getUrlEncoder();
byte[] textByte = new byte[0];
try {
textByte = data.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String encodedText = encoder.encodeToString(textByte);
Integer x = 0;
Integer len = encodedText.length();
Integer l = key.length();
String char_tmp = "";
String str = "";
// 循环拼接私钥md5后的字符,组装到待加密字串长度
for (Integer i = 0; i < len; i++) {
if (x == l) {
x = 0;
}
char_tmp += key.substring(x, x + 1);
x++;
}
for (Integer i = 0; i < len; i++) {
// ord() 函数返回字符串的首个字符的 ASCII 值。
// 给每个字符循环 私钥md5后的ASCII 与 与原文处理后的字符做异或运算
// 最后把 ASCII 值转成字符
str += (char) (ord(encodedText.substring(i, i + 1)) ^ ord(char_tmp.substring(i, i + 1)));
}
try {
textByte = str.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
encodedText = encoder.encodeToString(textByte);
return encodedText.replace("=", ""); // 可以在URL安全传输
}
/**
* 解密函数 V2
* By: Malu
*
* @param data
* @param key
* @return
*/
public String decrypt(String data, String key) {
key = md5(key);
Integer x = 0;
String data_tmp = "";
try {
byte[] decodedBytes = Base64.getUrlDecoder().decode(data);
data_tmp = new String(decodedBytes, "utf-8");
} catch (Exception $e) {
}
Integer len = data_tmp.length();
Integer l = key.length();
String char_tmp = "";
String str_tmp = "";
// 循环拼接私钥md5后的字符,组装到加密字串长度一样长
for (Integer i = 0; i < len; i++) {
if (x == l) {
x = 0;
}
char_tmp += key.substring(x, x + 1);
x++;
}
for (Integer i = 0; i < len; i++) {
// 把加密字串ASCII 与 私钥md5后的字符串ASCII 做异或运算
// 最后把 ASCII 值还原成字符
str_tmp += (chr(ord(data_tmp.substring(i, i + 1)) ^ ord(char_tmp.substring(i, i + 1))));
}
try {
byte[] decodedBytes = Base64.getUrlDecoder().decode(str_tmp);
data_tmp = new String(decodedBytes, "utf-8");
} catch (Exception $e) {
}
return data_tmp;
}
算法加强
以上实现了基础的加密解密过程,我们可以自由改进其算法,比如把md5替换成其他哈希算法,也可以加盐,双重md5,甚至可以通过另外接口做成协商密钥,让密文随机变化,来加强加密强度。
PHP加密传输库
加解密库 php-encrypted-transmission 添加到 composer.json 配置文件
$ composer require malu/php-encrypted-transmission
升级 composer
$ composer update
使用示例
// If you installed via composer, just use this code to require autoloader on the top of your projects.
require 'vendor/autoload.php';
// Using Medoo namespace
use Malu\Encrypted\Encrypted;
$data = ["hello","malu","bbq"];
// 加密输出
$encrypt_data = Encrypted::encrypt(json_encode($data), "34f7e6dd6acf03192d82f0337c8c54ba");
echo $encrypt_data;
// 解密输出
echo Encrypted::decrypt($encrypt_data, "34f7e6dd6acf03192d82f0337c8c54ba");