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));
}
}