java 中的 aes 加密算法

本文详细介绍了AES加密算法的发展历程及其核心实现细节。从1997年美国国家标准和技术研究所发起的征集活动开始,到Rijndael算法最终胜出成为AES标准,文章涵盖了AES算法的关键特性,包括支持的不同密钥长度及分组大小。此外,还提供了AES加密的具体实现方法,包括使用CBC模式和PKCS5/PKCS7填充机制的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

aes 加密算法

1997年1月2日美国国家标准和技术研究所(NSIT)发布了高级加密标准(AES—FIPS)的研发计划,并于同年9月12日正式发布了征集候选算法公告[18],希望确定一种保护敏感信息的公开、免费并且全球通用的算法作为AES,以代替DES。在征集公告中,NSIT对算法的基本要求是:算法必须是私钥体制的分组密码,支持128位分组长度和128、192、256 bits密钥长度。

经过三轮遴选,Rjindael最终胜出。

两个 helper 方法

String SEED = "ae@ci#LIjsc!668Ku";
byte[] key = getMd5Key(SEED.getBytes("UTF-8"));
byte[] key = getRawKey(SEED.getBytes("UTF-8"));

// md5 加密 seed
private static byte[] getMd5Key(byte[] bytes) throws Exception {
    MessageDigest messageDigest = null;
    messageDigest = MessageDigest.getInstance("MD5");
    messageDigest.reset();
    messageDigest.update(SEED.getBytes(DEFAULT_ENCODING));
    byte[] key = messageDigest.digest();
    return key;
}

// SHA1 加密
private static byte[] getRawKey(byte[] seed) throws Exception {
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    sr.setSeed(seed);
    kgen.init(128, sr);
    SecretKey sKey = kgen.generateKey();
    byte[] raw = sKey.getEncoded();

    return raw;
}

bcprov jar 包

org.bouncycastle.crypto.engines.AESFastEngine

AES (Rijndael) 算法的实现-快速版本,占用更多的内存

org.bouncycastle.crypto.modes.CBCBlockCipher

implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.

密码段链接(cipher block chaining,CBC)是一种操作分段密码的方式(将一段bit序列加密成一个单独的单元或分成一个密钥提供给整个部分)。密码段链接使用一定长度初始化向量(IV)。它的一个主要特点是完全依靠前面的密码文段来译码后面的内容。因此,整个过程的正确性决定于前面的部分。各部分的顺序必须保持正确。

在密码段链接中,每个明文段先用前面密文进行异或运算,然后加密。如果密文段顺序不发生改变,使用相同的密钥和初始化向量时,只有同样的密文段可以起作用。因为异或过程隐藏了原文,密码段链接要优于电子密码书模式。

理论上,两条信息使用相同的密钥加密会产生不同的初始化向量。所以初始化向量不需要保密,这会极大方便某些场合下的应用。

org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher

一个包装类, 允许分组密码用来逐一地处理数据然后填充. PaddedBufferedBlockCipher 在分组已满继续有添加内容时会输出一个分组,或者在调用 doFinal() 后输出.默认的填充机制是 PKCS5/PKCS7.
A wrapper class that allows block ciphers to be used to process data in a piecemeal fashion with padding. The PaddedBufferedBlockCipher outputs a block only when the buffer is full and more data is being added, or on a doFinal (unless the current block in the buffer is a pad block). The default padding mechanism used is the one outlined in PKCS5/PKCS7.

org.bouncycastle.crypto.params.KeyParameter

Example

/**
 * 加密与解密
 * @param encrypt true 加密, false 解密
 * @param inputBytes
 * @return
 * @throws Exception
 */
private byte[] transform(boolean encrypt, byte[] inputBytes) throws Exception {
    MessageDigest messageDigest = MessageDigest.getInstance("MD5");
    messageDigest.reset();
    messageDigest.update(privateKey.getBytes(DEFAULT_ENCODING));
    byte[] key = messageDigest.digest();

    BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
    cipher.init(encrypt, new KeyParameter(key));

    ByteArrayInputStream input = new ByteArrayInputStream(inputBytes);
    ByteArrayOutputStream output = new ByteArrayOutputStream();

    int inputLen;
    int outputLen;

    byte[] inputBuffer = new byte[1024];
    byte[] outputBuffer = new byte[cipher.getOutputSize(inputBuffer.length)];

    while ((inputLen = input.read(inputBuffer)) > -1) {
        outputLen = cipher.processBytes(inputBuffer, 0, inputLen, outputBuffer, 0);
        if (outputLen > 0) {
            output.write(outputBuffer, 0, outputLen);
        }
    }

    outputLen = cipher.doFinal(outputBuffer, 0);
    if (outputLen > 0) {
        output.write(outputBuffer, 0, outputLen);
    }

    return output.toByteArray();
}

javax.crypto 包

android 中可以使用 javax.crypto 包

import android.util.Base64;

import java.security.MessageDigest;

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

/**
 * Created by zhch on 2015-7-4.
 */
public class AESUtils {

    public static final String TAG = "AESUtils";
    private static final String DEFAULT_ENCODING = "UTF-8";

    private static final String SEED = "@P13ncryptK3Y!+";

    public static String encrypt(String clearText) {
        String content = null;
        try {
            byte[] key = getMd5Key(SEED.getBytes(DEFAULT_ENCODING));
            byte[] result = encrypt(key, clearText.getBytes(DEFAULT_ENCODING));

            byte[] base64Bytes = Base64.encode(result, Base64.DEFAULT);
            content = new String(base64Bytes, DEFAULT_ENCODING);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Log.d(TAG, "加密后的内容为:" + content);
        return content;

    }



    public static String decrypt(String encrypted) {
        try {
            byte[]  key = getMd5Key(SEED.getBytes(DEFAULT_ENCODING));
            byte[] base64Bytes = Base64.decode(encrypted, Base64.DEFAULT);

            byte[] result = decrypt(key, base64Bytes);
            String content = new String(result, DEFAULT_ENCODING);
            // Log.d(TAG, "解密后的内容为:" + content);
            return content;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    private static byte[] getMd5Key(byte[] bytes) throws Exception {
        MessageDigest messageDigest = null;
        messageDigest = MessageDigest.getInstance("MD5");
        messageDigest.reset();
        messageDigest.update(SEED.getBytes(DEFAULT_ENCODING));
        byte[] key = messageDigest.digest();
        return key;
    }

    private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(
                new byte[cipher.getBlockSize()]));
        byte[] encrypted = cipher.doFinal(clear);
        return encrypted;
    }

    private static byte[] decrypt(byte[] raw, byte[] encrypted)
            throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(
                new byte[cipher.getBlockSize()]));
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }



    public static void main(String[] aaa) {
        System.out.println("old: NStNPAKF6hmnrPJk70kA0g==");
        String enStr = encrypt("PASS:liya");
        System.out.println("new: " + enStr);
        System.out.println("new: " + encrypt("PASS:liya"));
        System.out.println("new: " + encrypt("PASS:liya"));
        System.out.println("dec: " + decrypt(enStr));

        String twoEn = encrypt("NStNPAKF6hmnrPJk70kA0g==");
        System.out.println("two: " + twoEn);
        System.out.println("tDe: " + decrypt(twoEn));
    }

}

refs:
AESFastEngine
密码段链接 cipher block chaining CBC
AES加密算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值