- 分块密码, 因此要求被加密对象长度为块长度的倍数
- 秘钥长度必须是16或者24或者32 分别对应AES-128, AES-192, AES-256
- 需要以某种形式将明文的长度编码
- 加密算法有cipher.NewCBCEncrypter, cipher.NewCFBEncrypter, 但是他们返回的对象分别是Block和Stream。 未比较块和流的优点缺点。
只要理解了这个过程代码非常好调。
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"unsafe"
)
// PCKS padding
func padKey(key []byte) []byte {
var targetKeySize = 16
if len(key) <= 16 {
targetKeySize = 16
} else if len(key) <= 24 {
targetKeySize = 24
} else {
targetKeySize = 32
}
if r := targetKeySize - len(key); r > 0 {
nkey := make([]byte, targetKeySize)
copy(nkey, key)
copy(nkey[len(key):], bytes.Repeat([]byte{byte(r)}, r))
key = nkey
}
return key[:targetKeySize]
}
func encrypt(source []byte, keyStr string) ([]byte, error) {
srcLength := len(source)
var usize uint64
headSize := int(unsafe.Sizeof(usize))
head := make([]byte, headSize, headSize+len(source))
source = append(head, source...)
len0 := len(source)
if r := len0 % aes.BlockSize; r > 0 {
len0 += aes.BlockSize - r
padding := make([]byte, aes.BlockSize-r)
if _, err := io.ReadFull(rand.Reader, padding); err != nil {
return nil, err
}
source = append(source, padding...)
}
buffer := make([]byte, len0+aes.BlockSize)
iv := buffer[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
usize = uint64(srcLength)
binary.BigEndian.PutUint64(source, usize)
block, ec := aes.NewCipher(padKey([]byte(keyStr)))
if ec != nil {
return nil, ec
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(buffer[aes.BlockSize:], source)
return buffer, nil
}
func decrypt(encbuffer []byte, keyStr string) ([]byte, error) {
iv := encbuffer[:aes.BlockSize]
out := encbuffer[aes.BlockSize:]
block, ec := aes.NewCipher(padKey([]byte(keyStr)))
if ec != nil {
return nil, ec
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(out, out)
usize := binary.BigEndian.Uint64(out)
var headSize = int(unsafe.Sizeof(usize))
return out[headSize : headSize+int(usize)], nil
}
func encTest(src, key string) {
ebuff, err := encrypt([]byte(src), key)
if err != nil {
panic(err)
}
dbuff, err := decrypt(ebuff, key)
if err != nil {
panic(err)
}
fmt.Printf("<%v> : len = %v (encrypted length=%v)\n",
string(dbuff),
len(dbuff),
len(ebuff),
)
fmt.Printf("<%v>\n", ebuff)
}
func main() {
src := []byte("hasta victoria siempre")
encrypted, err := encrypt(src, "klo")
if err != nil {
panic(err)
}
fmt.Printf("%v\n", encrypted)
fmt.Printf("len for encrypted: %v\n", len(encrypted))
decrypted, err := decrypt(encrypted, "klo")
if err != nil {
panic(err)
}
fmt.Printf("decrypted len is: %v\n", len(decrypted))
fmt.Printf("decrypted: %v\n", string(decrypted))
encTest("", "a")
encTest("", "ss")
encTest("a", "")
encTest("a", "aa")
}
reference link:
https://stackoverflow.com/questions/24072026/golang-aes-ecb-encryption
https://stackoverflow.com/questions/18817336/golang-encrypting-a-string-with-aes-and-base64
https://www.ctolib.com/topics-3631.html
http://www.cnblogs.com/lianghui66/archive/2013/03/07/2948494.html
http://gunn.me/2015/07/213/
http://www.cnblogs.com/lavin/p/5373188.html
https://segmentfault.com/a/1190000004151272
https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption/20946#20946
http://www.cnblogs.com/happyhippy/archive/2006/12/23/601357.html
https://baike.baidu.com/item/Diffie-Hellman
最后附上nodejs(其他工具的实现)
const crypto = require('crypto');
const assert = require('assert');
//buff
function padKey(buffStr){
let buffKey = Buffer.from(buffStr);
let targetLen = 16;
if(buffKey.length<=16){
targetLen = 16;
} else if(buffKey.length<=24){
targetLen = 24;
} else {
targetLen = 32;
}
let r = targetLen - buffKey.length;
if (r>0) {
let padding = Buffer.alloc(r, r);
buffKey = Buffer.concat([buffKey, padding]);
}
return buffKey.slice(0, targetLen);
}
function randInt8(){
return Math.floor(Math.random() * 256);
}
/// a buffer of length equals block-size
function randIv() {
let rv = Buffer.alloc(16, 0);
rv.forEach((c,i)=>{
rv[i] = randInt8();
});
return rv;
}
function makeCipherName(k){
let cipherName = 'aes-128-cbc'; //cipher block chain
switch(k.length){
case 16:
cipherName = 'aes-128-cbc';
break;
case 24:
cipherName = 'aes-192-cbc';
break;
case 32:
cipherName = 'aes-256-cbc';
break;
default:
throw new Error(`unknown key length ${k.length}`);
}
return cipherName;
}
function makeCipher(k, iv){
let algoName = makeCipherName(k);
return crypto.createCipheriv(algoName, k, iv);
}
function makeDecipher(k, iv){
let algoName = makeCipherName(k);
return crypto.createDecipheriv(algoName, k, iv);
}
// using uint64 as the head
function encrypt(source, keyStr){
let srcBuff = Buffer.from(source);
let headBuff = Buffer.alloc(8, 0)
headBuff.writeUInt32BE(srcBuff.length, 4);
srcBuff = Buffer.concat([headBuff, srcBuff]);
let r = srcBuff.length % 16;
if(r>0){
srcPadding = Buffer.alloc(16 - r, 0);
srcPadding.forEach((c,i)=>{srcPadding[i] = randInt8()});
}
let startiv = randIv();
let cipher = makeCipher(padKey(keyStr), startiv);
let buffs = [startiv];
buffs.push(cipher.update(srcBuff));
buffs.push(cipher.final());
return Buffer.concat(buffs);
}
function decrypt(chunk, keyStr){
if(Buffer.isBuffer(chunk)){
//nada
} else {
chunk = Buffer.from(chunk, 'hex');
}
//console.log("tot length is(encoded): %s", chunk.length);
let iv = chunk.slice(0, 16)
chunk = chunk.slice(16);
let cipher = makeDecipher(padKey(keyStr), iv);
let decoded = [];
decoded.push(cipher.update(chunk));
decoded.push(cipher.final());
decoded = Buffer.concat(decoded);
//console.log("decoded length is %s", decoded.length);
let len = decoded.readUInt32BE(4);
return decoded.slice(8, 8+len);
}
if(process.argv.length<4){
console.error("not enough parameters");
process.exit(-1);
}
//console.log(encrypt('hasta victoria siempre', 'kl1'));
let encrypted = encrypt(process.argv[2], process.argv[3]);
console.log(encrypted.toString('hex'));
console.log(encrypted.toString('utf8'));
let decrypted = decrypt(encrypted, process.argv[3]);
console.log(decrypted.toString());