3DES加解密的算法Java Python Golang
前言
最近做项目,发现一些报文需要加密,就简单研究了一些加密算法,包括国密和国际算法,顺便写了一个简单的对称加密算法应用,简单说明原理。实际工作中肯定更复杂,甚至需要专门的硬件配合才行。初步统计的国密和国际加密算法。
- 国密算法
SM1、SM2、SM3、SM4、SM7、SM9、ZUC
常用的SM2(非对称加密) SM3(摘要算法) SM4(对称加密)
- 非国密
RSA、ECC、DH(非对称)MD5、SHA(摘要算法) AES、DES、RC4(对称加密)
准备
一般而言,绝大多数报文和文件加密都是对称加密,毕竟计算资源消耗是非常恐怖的,而且可以通过其他方式增强安全性,比如非对称加密交换对称加密秘钥,秘钥随会话变化而变化,这不就是TLS的设计思想。
Java demo
以DES对称加密为例,但是DES加密的8位秘钥较容易被暴力破解,所以使用3DES加密为例,实际生产一般使用AES或者SM4等。
以外网的一个案例为例,笔者已经找不到最开始从哪里来的了
package com.feng.demo;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* SecretUtils {3DES加密解密的工具类 }
* @author William
* @date 2013-04-19
*/
public class SecretUtils {
public static void main(String[] args) {
String originStr = "abc";
String resultStr = Base64.getEncoder().encodeToString(encryptMode(originStr.getBytes(StandardCharsets.UTF_8)));
System.out.println(resultStr);
}
//定义加密算法,有DES、DESede(即3DES)、Blowfish
private static final String Algorithm = "DESede";
// private static final String PASSWORD_CRYPT_KEY = "2012";
private static final String PASSWORD_CRYPT_KEY = "2012PinganVitality075522628888ForShenZhenBelter075561869839";
/**
* 加密方法
* @param src 源数据的字节数组
* @return
*/
public static byte[] encryptMode(byte[] src) {
try {
SecretKey deskey = new SecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), Algorithm); //生成密钥
Cipher c1 = Cipher.getInstance(Algorithm); //实例化负责加密/解密的Cipher工具类
c1.init(Cipher.ENCRYPT_MODE, deskey); //初始化为加密模式
return c1.doFinal(src);
} catch (Exception e3) {
e3.printStackTrace();
}
return null;
}
/**
* 解密函数
* @param src 密文的字节数组
* @return
*/
public static byte[] decryptMode(byte[] src) {
try {
SecretKey deskey = new SecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), Algorithm);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.DECRYPT_MODE, deskey); //初始化为解密模式
return c1.doFinal(src);
} catch (Exception e3) {
e3.printStackTrace();
}
return null;
}
/*
* 根据字符串生成密钥字节数组
* @param keyStr 密钥字符串
* @return
* @throws UnsupportedEncodingException
*/
public static byte[] build3DesKey(String keyStr) throws UnsupportedEncodingException{
byte[] key = new byte[24]; //声明一个24位的字节数组,默认里面都是0
byte[] temp = keyStr.getBytes("UTF-8"); //将字符串转成字节数组
/*
* 执行数组拷贝
* System.arraycopy(源数组,从源数组哪里开始拷贝,目标数组,拷贝多少位)
*/
//如果temp不够24位,则拷贝temp数组整个长度的内容到key数组中
//如果temp大于24位,则拷贝temp数组24个长度的内容到key数组中
System.arraycopy(temp, 0, key, 0, Math.min(key.length, temp.length));
return key;
}
}
简单优化了一下代码,简单说明
1. 构建24字节长度的秘钥,3DES刚好是DES的3倍秘钥
2. 传入加密算法和偏移量,这里使用ECB模式,没有偏移量,如果使用CBC模式需要传入IV偏移量
3. 通过base64转为字符串
运行后:

这里有个注意事项,内容和结果必须为block块的整数倍长度,如果不够必须填充,只不过Java包装了,见com.sun.crypto.provider.CipherCore.
prepareInputBuffer

这个Math方法写的看不懂

然后可以看到是通过数组填充

Python demo
其实刚刚的代码运行的好好的,但是,毕竟有Python客户端,甚至有C go等语言,怎么办,自己再写一遍实现,正是这一次重写,发现Java封装了好的隐藏逻辑,比如秘钥一定要大于16位,太短了补0,Java可以运行,但是Python不可以。
简单Python字符串和字节数组处理:
#正常字符串使用''或""表示
# 字符串前b,表示是字节数组
b = b"aaa"
# 单引号
b = b'aaa'
# str to bytes
bytes('abc', encoding = 'utf8')
# an alternative method
str.encode('abc')
# bytes to str
str(b, encoding = 'utf-8')
bytes.decode(b)
安装des3加密包
pip install pycryptodome
然后根据3DES的规则编写代码
import base64
import sys
from Crypto.Cipher import DES3
#PASSWORD_CRYPT_KEY = '2012'
PASSWORD_CRYPT_KEY = b'2012PinganVitality075522628888ForShenZhenBelter075561869839'
def build_3des_key(key_str, key_len=24) -> bytes:
length = len(key_str)
if length < key_len:
key = key_str + chr(0) * (key_len - length)
return bytes(key, 'utf-8')
else:
return key_str[:key_len]
def build_src_fixed_len(src) -> str:
delivery_len = 8 - len(src) % 8
if delivery_len != 0:
return src + chr(delivery_len) * delivery_len
return src
def encrypt_mode(src) -> bytes:
cipher = DES3.new(build_3des_key(PASSWORD_CRYPT_KEY), DES3.MODE_ECB)
return cipher.encrypt(src)
def decrypt_mode(src):
cipher = DES3.new(build_3des_key(PASSWORD_CRYPT_KEY), DES3.MODE_ECB)
return cipher.decrypt(src)
args = sys.argv
print(args)
if args[1] == 'e':
src = build_src_fixed_len(args[2])
bytes_src = bytes(src, 'utf-8')
bytes_res = encrypt_mode(bytes_src)
bytes_res = base64.encodebytes(bytes_res)
print(str(bytes_res, encoding = 'utf-8'))
else:
bytes_src = base64.decodebytes(bytes(args[2], 'utf-8'))
bytes_res = decrypt_mode(bytes_src)
print(str(bytes_res, encoding = 'utf-8'))
然后执行命令:python secure_demo.py e 'abc'

可以看到跟Java结果一模一样,解密同理:
![]()
这里python有2个注意点,是Java封装后看不出来的:
1. 内容字符串长度必须是8的整数倍

2. 秘钥字符串长度必须超过8位,否则封装的内容是不对的

判断长度,前8位和中间8位;反转判断,如果一样则认为是DES加解密,这个在Java里面封装了处理逻辑,所以可以正常运行
3. 如果不够block的内容,怎么填充呢,是block-长度%block(定义为8)
当然也可以使用class特性,使用main方法运行,各种语言开始趋同进化了。
golang demo
仿照python和Java
package secure
import (
"crypto/des"
)
const PASSWORD_CRYPT_KEY string = "2012PinganVitality075522628888ForShenZhenBelter075561869839"
func buildSecureKey(key string) []byte {
bytes := make([]byte, 24)
copy(bytes, []byte(key))
//fmt.Println(string(bytes))
return bytes
}
func BuildSrcBytes(src []byte) []byte {
length := len(src)
deliveryLength := des.BlockSize - length%des.BlockSize
if deliveryLength != 0 {
deliverySlice := make([]byte, deliveryLength)
for i := 0; i < deliveryLength; i++ {
deliverySlice[i] = byte(deliveryLength)
}
bytes := append(src, deliverySlice...)
//fmt.Println(string(bytes))
return bytes
}
//fmt.Println(string(src))
return src
}
func EncryptMode(src []byte) []byte {
cipher, _ := des.NewTripleDESCipher(buildSecureKey(PASSWORD_CRYPT_KEY))
bytesOut := make([]byte, len(src))
//fmt.Println(src)
cipher.Encrypt(bytesOut, src)
//fmt.Println(bytesOut)
return bytesOut
}
func DecryptMode(src []byte) []byte {
cipher, _ := des.NewTripleDESCipher(buildSecureKey(PASSWORD_CRYPT_KEY))
bytesOut := make([]byte, len(src))
//fmt.Println(src)
cipher.Decrypt(bytesOut, src)
return bytesOut
}
然后写个main方法
package main
import (
"demo/secure"
"encoding/base64"
"fmt"
)
func main() {
originStr := "abc"
src := secure.BuildSrcBytes([]byte(originStr))
result := secure.EncryptMode(src)
fmt.Println(base64.StdEncoding.EncodeToString(result))
srcBase64 := "dRQLaDw5udI="
bytes, _ := base64.StdEncoding.DecodeString(srcBase64)
resOrigin := secure.DecryptMode(bytes)
fmt.Println(string(resOrigin))
}
执行后跟其他语言一致

总结
实际上仅仅是对称加密,实际上并不安全,不仅面临碰撞攻击,还面临着秘钥保存的问题,根据HTTPS的TLS密码设计,应该动态生成对称加密密钥,使用非对称加密算法交换密钥,根据会话动态变化。但是没有绝对的安全,实际上是加密和性能的取舍问题。非对称加密非常消耗计算性能,但是安全等级很高,对称加密对性能消耗较低,但是安全等级较低。
以TLSv1.2为例,先通过非对称加密(证书)交换对称加密的秘钥,加密回话报文。

更多推荐



所有评论(0)