AES过程

  1. 分块密码, 因此要求被加密对象长度为块长度的倍数
  2. 秘钥长度必须是16或者24或者32 分别对应AES-128, AES-192, AES-256
  3. 需要以某种形式将明文的长度编码
  4. 加密算法有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());



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值