JAVA与NodeJS实现AES加密

内容要点

本文实现java与nodejs的AES加密方式如下,并可实现java加密,nodejs解密或者nodejs加密,java解密

  • aes-128-ecb
  • aes-256-ecb
  • aes-128-cbc
  • aes-256-cbc

java实现AES

注意

Java本身限制密钥的长度最多128位,而AES256需要的密钥长度是256位,因此需要到Java官网上下载一个Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。在Java SE的下载页面下面的Additional Resources那里会有下载链接。下载后打开压缩包,里面有两个jar文件:
local_policy.jarUS_export_policy.jar
把这两个jar文件解压到JRE目录下的lib/security文件夹,覆盖原来的文件。这样Java就不再限制密钥的长度了,否则编译会报错:

java.security.InvalidKeyException: Illegal key size

import org.apache.commons.lang.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.AlgorithmParameterSpec;

public class EncodeUtil {

    public static void main(String[] args) throws Exception {
        //key是16进制,需要转换为bytes,转换后bytes长度为16,即aes128,如果bytes长度是32则是aes256
        //也就是说keybytes.length须满足16的整数倍
        String key128 = "c4b84456c1379bec99c4d1b7e9f13173";
        String key256 = "c4b84456c1379bec99c4d1b7e9f13173c4b84456c1379bec99c4d1b7e9f13173";
        //iv.length须满足16的整数倍
        byte[] iv = "abcdefgh12345678".getBytes("UTF-8");
        String content_str = "helloworld 你好";
        byte[] contentbytes = content_str.getBytes("utf-8");

        //ecb128 bytes
        byte[] encryptbytes = EncodeUtil.aesEncryptToECB(contentbytes,key128);
        byte[] decryptbytes = EncodeUtil.aesDecryptToECB(encryptbytes,key128);
        System.out.println(new String(decryptbytes,"utf-8"));

        //ecb256 bytes
        encryptbytes = EncodeUtil.aesEncryptToECB(contentbytes,key256);
        decryptbytes = EncodeUtil.aesDecryptToECB(encryptbytes,key256);
        System.out.println(new String(decryptbytes,"utf-8"));

        //ecb128 String
        String encryptString = EncodeUtil.aesEncryptToECB(content_str,key128);
        String decryptString = EncodeUtil.aesDecryptToECB(encryptString,key128);
        System.out.println(decryptString);

        //ecb256 String
        encryptString = EncodeUtil.aesEncryptToECB(content_str,key256);
        decryptString = EncodeUtil.aesDecryptToECB(encryptString,key256);
        System.out.println(decryptString);

        //cbc128 bytes
        encryptbytes = EncodeUtil.aesEncryptToCBC(contentbytes,key128,iv);
        decryptbytes = EncodeUtil.aesDecryptToCBC(encryptbytes,key128,iv);
        System.out.println(new String(decryptbytes,"utf-8"));

        //cbc256 bytes
        encryptbytes = EncodeUtil.aesEncryptToCBC(contentbytes,key256,iv);
        decryptbytes = EncodeUtil.aesDecryptToCBC(encryptbytes,key256,iv);
        System.out.println(new String(decryptbytes,"utf-8"));

        //cbc128 String
        encryptString = EncodeUtil.aesEncryptToCBC(content_str,key128,iv);
        decryptString = EncodeUtil.aesDecryptToCBC(encryptString,key128,iv);
        System.out.println(decryptString);

        //cbc256 String
        encryptString = EncodeUtil.aesEncryptToCBC(content_str,key256,iv);
        decryptString = EncodeUtil.aesDecryptToCBC(encryptString,key256,iv);
        System.out.println(decryptString);

    }

    /**
     * base 64 encode
     *
     * @param bytes 待编码的byte[]
     * @return 编码后的base 64 code
     */
    public static String base64Encode(byte[] bytes) {
        return new BASE64Encoder().encode(bytes);
    }

    /**
     * base 64 decode
     *
     * @param base64Code 待解码的base 64 code
     * @return 解码后的byte[]
     * @throws Exception
     */
    public static byte[] base64Decode(String base64Code) throws Exception {
        return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
    }

     /**
     * 验证密钥长度是否有效
     *
     * @param key 密钥bytes
     * @throws Exception
     */
    public static void checkkey(byte[] key) throws Exception {

        if(key.length != 16 && key.length != 32) {
            throw new Exception("密钥长度错误,key byte[]必须是16或者32位");
        }
    }

    /**
     * AES加密 aes-128/256-ecb
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的byte[]
     * @throws Exception
     */
    public static byte[] aesEncryptToECB(byte[] content, String encryptKey) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(encryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
        return cipher.doFinal(content);
    }



    /**
     * AES加密 aes-128/256-ecb
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的base64字符串
     * @throws Exception
     */
    public static String aesEncryptToECB(String content, String encryptKey) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(encryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
        return base64Encode(cipher.doFinal(content.getBytes("utf-8")));
    }


    /**
     * AES解密 aes-128/256-ecb
     *
     * @param content 待解密的byte[]
     * @param decryptKey   解密密钥
     * @return 解密后的byte[]
     * @throws Exception
     */
    public static byte[] aesDecryptToECB(byte[] content, String decryptKey) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(decryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
        byte[] decryptBytes = cipher.doFinal(content);
        return decryptBytes;
    }



    /**
     * AES解密 aes-128/256-ecb
     *
     * @param content 待解密的byte[]
     * @param decryptKey   解密密钥
     * @return 解密后的String
     * @throws Exception
     */
    public static String aesDecryptToECB(String content, String decryptKey) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(decryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
        byte[] decryptBytes = cipher.doFinal(base64Decode(content));
        return new String(decryptBytes,"utf-8");
    }


    /**
     * AES加密 aes-128/256-cbc
     *
     * @param content 待解密的byte[]
     * @param encryptKey   加密密钥
     * @param iv 偏移
     * @return 解密后的byte[]
     * @throws Exception
     */
    public static byte[] aesEncryptToCBC(byte[] content, String encryptKey,byte[] iv) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(encryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //算法参数
        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),paramSpec);
        return cipher.doFinal(content);
    }

    /**
     * AES解密 aes-128/256-cbc
     *
     * @param content 待解密的byte[]
     * @param decryptKey   解密密钥
     * @param iv 偏移
     * @return 解密后的byte[]
     * @throws Exception
     */
    public static byte[] aesDecryptToCBC(byte[] content, String decryptKey,byte[] iv) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(decryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //算法参数
        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),paramSpec);
        return cipher.doFinal(content);
    }

    /**
     * AES加密 aes-128/256-cbc
     *
     * @param content 待解密的byte[]
     * @param encryptKey   加密密钥
     * @param iv 偏移
     * @return 解密后的byte[]
     * @throws Exception
     */
    public static String aesEncryptToCBC(String content, String encryptKey,byte[] iv) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(encryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //算法参数
        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),paramSpec);
        return base64Encode(cipher.doFinal(content.getBytes("utf-8")));
    }

    /**
     * AES解密 aes-128/256-cbc
     *
     * @param content 待解密的byte[]
     * @param decryptKey   解密密钥
     * @param iv 偏移
     * @return 解密后的byte[]
     * @throws Exception
     */
    public static String aesDecryptToCBC(String content, String decryptKey,byte[] iv) throws Exception {
        byte[] key = org.apache.commons.codec.binary.Hex.decodeHex(decryptKey.toCharArray());
        checkkey(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //算法参数
        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),paramSpec);
        byte[] decryptBytes = cipher.doFinal(base64Decode(content));
        return new String(decryptBytes,"utf-8");
    }
}

nodejs

说明

以下nodejs代码来源于 aes-cross项目

/**
 * "AES/cbc/pkcs5Padding" encription and decription.
 * setAutoPadding(true) is actually pkcs5Padding,.
 */
'use strict';

var crypto = require('crypto');

var CBC = 'cbc';
var ECB = 'ecb';
var NULL_IV = new Buffer([]);

var IV = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
var cipherMode = ECB;
var keySize = 128;
var algorithm;
setAlgorithm();
var outputEncoding = 'base64';
var inputEncoding = 'utf8';

function setAlgorithm() {
    algorithm = 'aes-' + keySize + '-' + cipherMode;
}

function setCipherMode(mode) {
    if (mode !== CBC && mode !== ECB) {
        throw ('AES.setCipherMode error: ' + mode);
    }
    cipherMode = mode;
    setAlgorithm();
}

function setKeySize(size) {
    if (size !== 128 && size !== 256) {
        throw ('AES.setKeySize error: ' + size);
    }
    keySize = size;
    setAlgorithm();
    // console.log('setKeySize:%j',keySize);
}

/**
 * the key must match the keySize/8 , like:16 ,32
 * @param  {Buffer} key
 * @return {}
 */
function checkKey(key) {
    if (!key) {
        throw 'AES.checkKey error: key is null ';
    }
    if (key.length !== (keySize / 8)) {
        throw 'AES.checkKey error: key length is not ' + (keySize / 8) + ': ' + key.length;
    }
}

/**
 * buffer/bytes encription
 * @param  {Buffer} buff
 * @param  {Buffer} key  the length must be 16 or 32
 * @param  {Buffer} [newIv]   default is [0,0...0]
 * @return {encripted Buffer}
 */
function encBytes(buff, key, newIv) {
    checkKey(key);
    var iv = newIv || IV;
    if (cipherMode === ECB) iv = NULL_IV;
    var cipher = crypto.createCipheriv(algorithm, key, iv);
    cipher.setAutoPadding(true);
    var re = Buffer.concat([cipher.update(buff), cipher.final()]);
    // console.log('enc re:%s,len:%d', printBuf(re), re.length);
    return re;
}

/**
 * text encription
 * @param  {string} text
 * @param  {Buffer} key         the length must be 16 or 32
 * @param  {Buffer} [newIv]       default is [0,0...0]
 * @param  {string} [input_encoding]  ["utf8" -default,"ascii","base64","binary"...](https://nodejs.org/api/buffer.html#buffer_buffer)
 * @param  {string} [output_encoding] ["base64" -default,"hex"]
 * @return {string}                 encription result
 */
function encText(text, key, newIv, input_encoding, output_encoding) {
    checkKey(key);
    var iv = newIv || IV;
    if (cipherMode === ECB) iv = NULL_IV;
    var inEncoding = input_encoding || inputEncoding;
    var outEncoding = output_encoding || outputEncoding;
    var buff = new Buffer(text, inEncoding);
    var out = encBytes(buff, key, iv);
    var re = new Buffer(out).toString(outEncoding);
    return re;
}

/**
 * buffer/bytes decription
 * @param  {Buffer} buff
 * @param  {Buffer} key  the length must be 16 or 32
 * @param  {Buffer} [newIv] default is [0,0...0]
 * @return {encripted Buffer}
 */
function decBytes(buff, key, newIv) {
    checkKey(key);
    var iv = newIv || IV;
    if (cipherMode === ECB) iv = NULL_IV;
    var decipher = crypto.createDecipheriv(algorithm, key, iv);
    decipher.setAutoPadding(true);
    var out = Buffer.concat([decipher.update(buff), decipher.final()]);
    return out;
}

/**
 * text decription
 * @param  {string} text
 * @param  {Buffer} key         the length must be 16 or 32
 * @param  {Buffer} [newIv]       default is [0,0...0]
 * @param  {string} [input_encoding]  ["utf8" - default,"ascii","base64","binary"...](https://nodejs.org/api/buffer.html#buffer_buffer)
 * @param  {string} [output_encoding] ["base64"- default ,"hex"]
 * @return {string}                 decription result
 */
function decText(text, key, newIv, input_encoding, output_encoding) {
    checkKey(key);
    var iv = newIv || IV;
    if (cipherMode === ECB) iv = NULL_IV;
    var inEncoding = input_encoding || inputEncoding;
    var outEncoding = output_encoding || outputEncoding;
    var buff = new Buffer(text, outEncoding);
    var re = new Buffer(decBytes(buff, key, iv)).toString(inEncoding);
    return re;
}


exports.setCipherMode = setCipherMode;
exports.setKeySize = setKeySize;
exports.encText = encText;
exports.encBytes = encBytes;
exports.decText = decText;
exports.decBytes = decBytes;

// 以下为测试部分
// //key是16进制,需要转换为buffer,转换后buffer长度为16,即aes128,如果buffer长度是32则是aes256
// var key = new Buffer("c4b84456c1379bec99c4d1b7e9f13173", 'hex');
// var key256 = new Buffer("c4b84456c1379bec99c4d1b7e9f13173c4b84456c1379bec99c4d1b7e9f13173", 'hex');
// var str = "helloworld 你好";
// var buffer = new Buffer(str,"utf8");
//
// //aes-ecb-128 buffer
// var buffer_encrypt = encBytes(buffer,key);
// var crypto_buffer =decBytes(buffer_encrypt,key);
// var str = crypto_buffer.toString();
// console.log(str);
//
// //aes-ecb-128 string
// var text_encrypt = encText(str,key);
// var text_decrypt =decText(text_encrypt,key);
// console.log(text_decrypt);
//
// text_encrypt = encText(str,key,null,'utf8','base64');
// text_decrypt =decText(text_encrypt,key,null,'utf8','base64');
// console.log(text_decrypt);
//
// //aes-cbc-128 buffer
// setCipherMode(CBC);
// var iv = new Buffer("abcdefgh12345678","utf8");//字符串一定是16位
// buffer_encrypt = encBytes(buffer,key,iv);
// crypto_buffer =decBytes(buffer_encrypt,key,iv);
// str = crypto_buffer.toString();
// console.log(str);
//
// //aes-cbc-128 string
// text_encrypt = encText(str,key,iv);
// text_decrypt =decText(text_encrypt,key,iv);
// console.log(text_decrypt);
//
// text_encrypt = encText(str,key,iv,'utf8','base64');
// text_decrypt =decText(text_encrypt,key,iv,'utf8','base64');
// console.log(text_decrypt);
//
//
// //aes-ecb-256 buffer
// setKeySize(256);
// setCipherMode(ECB);
// buffer_encrypt = encBytes(buffer,key256);
// crypto_buffer =decBytes(buffer_encrypt,key256);
// str = crypto_buffer.toString();
// console.log("256=="+str);

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268