沙滩星空的博客沙滩星空的博客

AES对称加密

案例

采用 AES 对称加密。对于加密密钥,直接将字符审强制转换为 128byte的二进制使用的。其中:

加密算法: AES-128 CBC 方式
初始化向量 IV: 和加密密钥相同
BlockSize: 16
补码方式: PKCS5Padding (padding byte=blockSize len(待加密字符串)%blockSize)加密结果编码方式:16 进制

示例

原价格: 2000(单位是分/CPM)
待加密字符串:"2000"
加密密匙:"6213FC1A2C51C632" (正式密钥)
加密后字符串:"b9996b4587b67ef45f2336c963f991c0"

解析

  • AES: 加解密算法
  • CBC: 数据分组模式
  • PKCS5Padding: 数据按照一定的大小进行分组,最后分剩下那一组,不够长度,就需要进行补齐

拿到一个原始数据以后,首先需要对数据进行分组,分组以后如果长度不满足分组条件,需要进行补齐,最后形成多个分组,在使用加解密算法,对这多个分组进行加解密。所以这个过程中,AES,CBC,PKCS5Padding 缺一不可

IV偏移量设置: ECB模式不需要设置, CBC模式需要设置

ECB 模式的分组强烈不建议使用

如果是 JAVA 语言指定算法,写的是 AES/CBC/PKCS5Padding

PHP实现

示例:

function pkcsPadding($str, $blocksize)
    {
        $pad = $blocksize - (strlen($str) % $blocksize);
        return $str . str_repeat(chr($pad), $pad);
    }


function unPkcsPadding($str)
    {
        $pad = ord($str{strlen($str) - 1});
        if ($pad > strlen($str)) {
            return false;
        }
        return substr($str, 0, -1 * $pad);
    }

$key = "6213FC1A2C51C632";
$method = "AES-128-CBC";
$ivlen = openssl_cipher_iv_length($method);
echo "---{$method}--iv length must be: $ivlen \n";
echo "---key({$key})--length is ".strlen($key)."\n";

$str = pkcsPadding("2000", 16);
$encrypted = openssl_encrypt($str, $method, $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $key);
$encrypted  = bin2hex($encrypted);
echo "-------encrypted--------:";
echo $encrypted . "\n";

$destr = openssl_decrypt(hex2bin($encrypted), $method, $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $key);
echo "-------decrypt--------:";
echo unPkcsPadding($destr)."\n";

输出:

---AES-128-CBC--iv length must be: 16
---key(6213FC1A2C51C632)--length is 16
-------encrypted--------:b9996b4587b67ef45f2336c963f991c0
-------decrypt--------:2000

Go实现

package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "encoding/hex"
    "errors"
    "fmt"
    "os"
)

var paddingErr error = errors.New("Error Padding")

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
    padding := blockSize - len(ciphertext)%blockSize
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte, blockSize int) ([]byte, error) {
    length := len(origData)
    // 去掉最后一个字节 unpadding 次
    unpadding := int(origData[length-1])
    if unpadding > 0 && unpadding <= blockSize {
        return origData[:(length - unpadding)], nil
    }
    return origData[:], paddingErr
}

func AesCBCEncrypte(s []byte, key string) string {
    keybytes := []byte(key)
    plaintext := PKCS5Padding([]byte(s), aes.BlockSize)
    block, _ := aes.NewCipher(keybytes[:aes.BlockSize])

    mode := cipher.NewCBCEncrypter(block, keybytes[:aes.BlockSize])
    crypted := make([]byte, len(plaintext))
    mode.CryptBlocks(crypted, plaintext)
    return string(hex.EncodeToString(crypted))
}

func AesCBCDecrypte(decrypte string, key string) (string, error) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Fprintf(os.Stderr, "error string:%s key:%s err:%v\n", decrypte, key, err)
        }
    }()

    if (len(decrypte) % (aes.BlockSize * 2)) != 0 {
        return "", errors.New("decrypte data too short")
    }
    keybytes := []byte(key)
    decrypteData, err := hex.DecodeString(decrypte)
    if err != nil {
        return "", errors.New("decrypte data format error")
    }

    block, _ := aes.NewCipher(keybytes[:aes.BlockSize])
    mode := cipher.NewCBCDecrypter(block, keybytes[:aes.BlockSize])

    decryptedData := make([]byte, len(decrypteData))
    mode.CryptBlocks(decryptedData, decrypteData)
    decryptedData, err = PKCS5UnPadding(decryptedData, aes.BlockSize)
    if err != nil {
        return "", err
    }
    return string(decryptedData), nil
}

func main() {
    data := "2000"
    key := "6213FC1A2C51C632"
    encrypt := AesCBCEncrypte([]byte(data), key)
    fmt.Println("encrypt:", encrypt)
    decrypt, _ := AesCBCDecrypte(encrypt, key)
    fmt.Println("decrypt:", decrypt)
}

运行输出:

encrypt: b9996b4587b67ef45f2336c963f991c0
decrypt: 2000

JAVA实现

package com.adget.server.common;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * AES 是一种可逆加密算法,对用户的敏感信息加密处理 对原始数据进行AES加密后,在进行Base64编码转化
 */
public class AesCBCUtils {

    private static String encrypt(byte[] sSrc, String sKey) throws Exception {
        if (sKey == null || sSrc == null) {
            return null;
        }
        return byte2hex(encryptByte(sSrc, sKey)).toLowerCase();
    }

    // 加密
    public static String encrypt(String sSrc, String sKey) throws Exception {
        if (sKey == null || sSrc == null) {
            return null;
        }
        return encrypt(sSrc.getBytes(), sKey);
    }

    private static byte[] encryptByte(byte[] sSrc, String sKey) throws Exception {
        if (sKey == null || sSrc == null) {
            return null;
        }
        // 判断Key是否为16位
        if (sKey.length() != 16) {
            return null;
        }
        byte[] raw = sKey.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(sKey.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        return cipher.doFinal(sSrc);
    }

    private static String decrypt(byte[] sSrc, String sKey) throws Exception {
        if (sKey == null || sSrc == null) {
            return null;
        }
        return new String(decryptByte(sSrc, sKey));
    }

    private static byte[] decryptByte(byte[] sSrc, String sKey) throws Exception {
        try {
            // 判断Key是否正确
            if (sKey == null || sSrc == null) {
                return null;
            }
            // 判断Key是否为16位
            if (sKey.length() != 16) {
                return null;
            }
            byte[] raw = sKey.getBytes();// "ASCII"
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(sKey.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            try {
                return cipher.doFinal(sSrc);
            } catch (Exception e) {
                return null;
            }
        } catch (Exception ex) {
            return null;
        }
    }

    // 解密
    public static String decrypt(String sSrc, String sKey) throws Exception {
        if (sKey == null || sSrc == null) {
            return null;
        }
        return decrypt(hex2byte(sSrc), sKey);
    }

    private static byte[] hex2byte(String strhex) {
        if (strhex == null) {
            return null;
        }
        int l = strhex.length();
        if (l % 2 == 1) {
            return null;
        }
        byte[] b = new byte[l / 2];
        for (int i = 0; i != l / 2; i++) {
            b[i] = (byte) Integer.parseInt(strhex.substring(i * 2, i * 2 + 2), 16);
        }
        return b;
    }

    private static String byte2hex(byte[] b) {
        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++) {
            stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (stmp.length() == 1) {
                hs = hs + "0" + stmp;
            } else {
                hs = hs + stmp;
            }
        }
        return hs.toUpperCase();
    }

    public static void main(String[] arg) {
        try {
            // 需要加密的字串
            String cSrc = "2000";
            String sKey = "d6797b0f95f8ead1";
            System.out.println("加密前的字串是:" + cSrc);
            // 加密
            String enString = encrypt(cSrc, sKey);
            System.out.println("加密后的字串是:" + enString);
            // 解密
            String DeString = decrypt(enString, sKey);
            System.out.println("解密后的字串是:" + DeString);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

DES对称加密介绍 http://blog.catmes.com/archives/des-encrypt.html
AES/CBC/PKCS5Padding 到底是什么 https://xie.infoq.cn/article/7e93850ddb9a6170bd49cd28c
PHP 报错openssl_encrypt(): IV passed is 32 bytes long... https://blog.csdn.net/Anne_01/article/details/128040891

未经允许不得转载:沙滩星空的博客 » AES对称加密

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址