起因是想实现oracle兼容的加解密算法,结果折腾了好几天也没有搞定兼容的3des算法.
副产品是把aes的各种场景验证了一遍.
之前没有密码学基础,通过折腾,稍微了解了一点.AES是比3des更先进的加密算法,虽然现在也不可靠了.
加密的块处理模式分为ECB和CBC. ECB因为不安全,已经废弃.如果不考虑和php兼容,那么可以不用了.
块处理就涉及一个填充模式,常见的填充模式,有补0(ZeroPadding)和补位数(PKcs5/PKcs7)
AES的兼容性相对比较好.可以先在线测试一下,获得比较基准.http://tool.chacuo.net/cryptaes
下面分别上代码(差别不大,但是分开来看得比较清楚)
先来ECB的,虽然已经淘汰了, 网上搜索的很多反而是这个.
js的,需要下载CrypoJS库. 注意解密的是base格式的str.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<script src="rollups/aes.js"></script>
<script src="components/pad-zeropadding.js"></script>
<script src="components/mode-ecb.js"></script>
<body>
<script>
var key = CryptoJS.enc.Utf8.parse("abcdefgh12345678");
var plaintText = 'www.baidu.com'; // 明文
//var plaintText = 'www.baidu.com'; // 明文
var encryptedData = CryptoJS.AES.encrypt(plaintText, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
//padding: CryptoJS.pad.ZeroPadding
});
console.log("加密前:"+plaintText);
console.log("加密后:"+encryptedData);
encryptedData = encryptedData.ciphertext.toString();
var encryptedHexStr = CryptoJS.enc.Hex.parse(encryptedData);
console.log("解密前hex:"+encryptedHexStr);
var encryptedBase64Str = CryptoJS.enc.Base64.stringify(encryptedHexStr);
console.log("解密前:"+encryptedBase64Str);
var decryptedData = CryptoJS.AES.decrypt(encryptedBase64Str, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
var decryptedStr = decryptedData.toString(CryptoJS.enc.Utf8);
console.log("解密后:"+decryptedStr);
</script>
</body>
</html>
golang的. 比较啰嗦.封装层次太低,padding函数也要自己写.
/*
描述 : golang AES/ECB/PKCS5|Zero 加密解密
date : 2016-04-08
*/
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
"strings"
)
func main() {
/*
*src 要加密的字符串
*key 用来加密的密钥 密钥长度可以是128bit、192bit、256bit中的任意一个
*16位key对应128bit
*/
src := "www.baidu.com"
key := "abcdefgh12345678"
crypted := AesEncrypt(src, key)
AesDecrypt(crypted, []byte(key))
}
func AesDecrypt(crypted, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
fmt.Println("err is:", err)
}
blockMode := NewECBDecrypter(block)
origData := make([]byte, len(crypted))
blockMode.CryptBlocks(origData, crypted)
//origData = PKCS5UnPadding(origData)
origData = ZeroUnPadding(origData)
fmt.Println("source is :", origData, string(origData))
return origData
}
func AesEncrypt(src, key string) []byte {
block, err := aes.NewCipher([]byte(key))
if err != nil {
fmt.Println("key error1", err)
}
if src == "" {
fmt.Println("plain content empty")
}
ecb := NewECBEncrypter(block)
content := []byte(src)
//content = PKCS5Padding(content, block.BlockSize())
content = ZeroPadding(content, block.BlockSize())
crypted := make([]byte, len(content))
ecb.CryptBlocks(crypted, content)
fmt.Println("base64 result:", base64.StdEncoding.EncodeToString(crypted))
return crypted
}
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize//需要padding的数目
//只要少于256就能放到一个byte中,默认的blockSize=16(即采用16*8=128, AES-128长的密钥)
//最少填充1个byte,如果原文刚好是blocksize的整数倍,则再填充一个blocksize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)//生成填充的文本
return append(ciphertext, padtext...)
}
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{0}, padding)//用0去填充
return append(ciphertext, padtext...)
}
func ZeroUnPadding(origData []byte) []byte {
return bytes.TrimFunc(origData,
func(r rune) bool {
return r == rune(0)
})
}
type ecb struct {
b cipher.Block
blockSize int
}
func newECB(b cipher.Block) *ecb {
return &ecb{
b: b,
blockSize: b.BlockSize(),
}
}
type ecbEncrypter ecb
// NewECBEncrypter returns a BlockMode which encrypts in electronic code book
// mode, using the given Block.
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
return (*ecbEncrypter)(newECB(b))
}
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
for len(src) > 0 {
x.b.Encrypt(dst, src[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
}
type ecbDecrypter ecb
// NewECBDecrypter returns a BlockMode which decrypts in electronic code book
// mode, using the given Block.
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
return (*ecbDecrypter)(newECB(b))
}
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
for len(src) > 0 {
x.b.Decrypt(dst, src[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
}
python的. 还行.不过加解密的对象是byte
#coding=utf-8
#AES-demo AES/ECB/PKCS5|Zero
#ECB不安全.建议CBC
import base64
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
def ByteToHex( bins ):
"""
Convert a byte string to it's hex string representation e.g. for output.
"""
return ''.join( [ "%02X" % x for x in bins ] ).strip()
'''
采用AES对称加密算法
'''
# str不是16的倍数那就补足为16的倍数. ZeroPadding
'''
在PKCS5Padding中,明确定义Block的大小是8位
而在PKCS7Padding定义中,对于块的大小是不确定的,可以在1-255之间
PKCS #7 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。
假定块长度为 8,数据长度为 9,
数据: FF FF FF FF FF FF FF FF FF
PKCS7 填充: FF FF FF FF FF FF FF FF FF 01 01 01 01 01 01 01 ?应该是填充01
python3:填充bytes(这个说法不对,AES的参数是字符串,不是byte)
length = 16 - (len(data) % 16)
data += bytes([length])*length
python2:填充字符串
length = 16 - (len(data) % 16)
data += chr(length)*length
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
'''
def add_to_16(value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value) # 返回bytes
def ZeroPadding(value,bs):
while len(value) % bs != 0:
value += '\0'
return str.encode(value) # 返回bytes
def PKCS7Padding(value,bs):
pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)#PKS7
return str.encode(pad(value)) # 返回bytes
def PKCS7UnPadding(value):
#value = value[:-value[-1]]
unpad = lambda s : s[0:-ord(s[-1])] #获得数据的长度,截取
return unpad(value)
#加密方法
def encrypt_oracle():
# 秘钥
key = 'abcdefgh12345678'
# 待加密文本
text = 'www.baidu.com'
# 初始化加密器
aes = AES.new(add_to_16(key), AES.MODE_ECB)
bs = AES.block_size
pad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)#PKS7 就是pkcs7padding
#先进行aes加密
#Zeropadding
#encrypt_aes = aes.encrypt(add_to_16(text))
#Pkcs7 padding
encrypt_aes = aes.encrypt(str.encode(pad2(text)))
#转为hex
print(ByteToHex(encrypt_aes)) #转为字符串
print(b2a_hex(encrypt_aes)) #b''
#用base64转成字符串形式
encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') # 执行加密并转码返回bytes
print(encrypted_text)
#和js的 结果相同 http://tool.chacuo.net/cryptaes
return encrypted_text
#解密方法
def decrypt_oralce(text):
# 秘钥
key = 'abcdefgh12345678'
# 密文
# 初始化加密器
aes = AES.new(add_to_16(key), AES.MODE_ECB)
#优先逆向解密base64成bytes
base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
#
decrypted_text = str(aes.decrypt(base64_decrypted),encoding='utf-8') # 执行解密密并转码返回str
unpad = lambda s : s[0:-ord(s[-1])] #pkcs7unpadding比较简单.
print(unpad(decrypted_text))
if __name__ == '__main__':
en=encrypt_oracle()
decrypt_oralce(en)