前言
和不同语言的第三方对接不可避免的遇到语言不一致的问题,比如加解密,有关于 golang 的 CBC/ECB/CFB 加密方法,这篇博客写的很全 传送门->
本文主要介绍 java AES 中出现的随机算法 SHA1PRNG 生成key,在 golang 中的实现方法
描述
先来看 java 代码
// java 代码
// content:test123
// encryptKey:123456
// 加密结果为:668C826342B8703D86E8BBF404610499
public static byte[] encrypt(String content, String encryptKey) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(encryptKey.getBytes());
kgen.init(128, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES"); // AES 和 AES/ECB/PKCS5Padding 等价
byte[] byteContent = content.getBytes("utf-8");
cipher.init(1, key);
byte[] result = cipher.doFinal(byteContent);
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
然后来看 golang 代码,示例1:
// go 代码
// content:test123
// encryptKey:123456
// 加密结果为:A794E6B031BFA05023B8C4577CA60595
// 如果直接用下面这个标准方法加密,得出来的结果和 java 中是不一致的,是因为对 key 缺少类似java SHA1PRNG的处理
func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) {
cipher, _ := aes.NewCipher(generateKey(key))
length := (len(origData) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, origData)
pad := byte(len(plain) - len(origData))
for i := len(origData); i < len(plain); i++ {
plain[i] = pad
}
encrypted = make([]byte, len(plain))
// 分组分块加密
for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted
}
示例2:
// content:test123
// encryptKey:123456
// 加密结果为:668C826342B8703D86E8BBF404610499
// 此时就和 java 结果相对应了,解密也一样对 key 加一步处理就行
func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) {
key, _ = AesSha1prng(key, 128) // 比示例一多出这一步
cipher, _ := aes.NewCipher(generateKey(key))
length := (len(origData) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, origData)
pad := byte(len(plain) - len(origData))
for i := len(origData); i < len(plain); i++ {
plain[i] = pad
}
encrypted = make([]byte, len(plain))
// 分组分块加密
for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted
}
// 模拟 java SHA1PRNG 处理
func AesSha1prng(keyBytes []byte, encryptLength int) ([]byte, error) {
hashs := Sha1(Sha1(keyBytes))
maxLen := len(hashs)
realLen := encryptLength / 8
if realLen > maxLen {
return nil, errors.New("invalid length!")
}
return hashs[0:realLen], nil
}
func Sha1(data []byte) []byte {
h := sha1.New()
h.Write(data)
return h.Sum(nil)
}
func generateKey(key []byte) (genKey []byte) {
genKey = make([]byte, 16)
copy(genKey, key)
for i := 16; i < len(key); {
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
genKey[j] ^= key[i]
}
}
return genKey
}
加解密完整demo(2021-08-26更新)
有同学说示例不完整,在此补充一下吧 ~~
package test
import (
"crypto/aes"
"crypto/sha1"
"encoding/hex"
"errors"
"testing"
)
func Test_Crypto(t *testing.T) {
str := []byte("test123")
key := []byte("123456")
// 加密
res, err := AesEncryptECB(str, key)
if err != nil {
t.Fatal(err)
}
t.Log(hex.EncodeToString(res)) // 输出:668c826342b8703d86e8bbf404610499
// 解密
de, err := AesDecryptECB(res, key)
if err != nil {
t.Fatal(err)
}
t.Log(string(de))
}
// content:test123
// encryptKey:123456
// 加密结果为:668C826342B8703D86E8BBF404610499
// 此时就和 java 结果相对应了,解密也一样对 key 加一步处理就行
func AesEncryptECB(src []byte, key []byte) ([]byte, error) {
key, err := AesSha1prng(key, 128) // 比示例一多出这一步
if err != nil {
return nil, err
}
cipher, _ := aes.NewCipher(generateKey(key))
length := (len(src) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, src)
pad := byte(len(plain) - len(src))
for i := len(src); i < len(plain); i++ {
plain[i] = pad
}
encrypted := make([]byte, len(plain))
// 分组分块加密
for bs, be := 0, cipher.BlockSize(); bs <= len(src); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted, nil
}
func AesDecryptECB(encrypted []byte, key []byte) ([]byte, error) {
key, err := AesSha1prng(key, 128) // 比示例一多出这一步
if err != nil {
return nil, err
}
cipher, _ := aes.NewCipher(generateKey(key))
decrypted := make([]byte, len(encrypted))
//
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return decrypted[:trim], nil
}
// 模拟 java SHA1PRNG 处理
func AesSha1prng(keyBytes []byte, encryptLength int) ([]byte, error) {
hashs := Sha1(Sha1(keyBytes))
maxLen := len(hashs)
realLen := encryptLength / 8
if realLen > maxLen {
return nil, errors.New("invalid length!")
}
return hashs[0:realLen], nil
}
func Sha1(data []byte) []byte {
h := sha1.New()
h.Write(data)
return h.Sum(nil)
}
func generateKey(key []byte) (genKey []byte) {
genKey = make([]byte, 16)
copy(genKey, key)
for i := 16; i < len(key); {
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
genKey[j] ^= key[i]
}
}
return genKey
}
总结
总的来说两者差不多,但是直接使用 golang 中的 AES ECB来进行加解密是有问题的,这点从在线加解密网站也可以看出来,对于 java 中进行了 SHA1PRNG 处理数据是结果是对应不上的!
参考博文:golang:实现Java 中的 AES SHA1PRNG算法_OceanStar的博客-CSDN博客
感谢您的阅读,欢迎点赞、关注、收藏!如果文章中有任何错误,或者您有更好的理解和建议,也欢迎和我联系!