[加解密]js/python/golang 兼容AES(CBC/ECB)加解密(1)--ECB模式

  起因是想实现oracle兼容的加解密算法,结果折腾了好几天也没有搞定兼容的3des算法.

  副产品是把aes的各种场景验证了一遍.

  之前没有密码学基础,通过折腾,稍微了解了一点.AES是比3des更先进的加密算法,虽然现在也不可靠了.

  加密的块处理模式分为ECB和CBC.  ECB因为不安全,已经废弃.如果不考虑和php兼容,那么可以不用了.

  块处理就涉及一个填充模式,常见的填充模式,有补0(ZeroPadding)和补位数(PKcs5/PKcs7)

  AES的兼容性相对比较好.可以先在线测试一下,获得比较基准.http://tool.chacuo.net/cryptaes

  下面分别上代码(差别不大,但是分开来看得比较清楚)

   先来ECB的,虽然已经淘汰了, 网上搜索的很多反而是这个.

   js的,需要下载CrypoJS库. 注意解密的是base格式的str.

  
  
  
      
    Title  
  
  
  
  
  
  

  
  
  
  

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)


   

你可能感兴趣的:(python,go,javascript)