1 密钥生成
1、 AES密钥生成:可以随机生成,用于AES加密,长度为16位,可以用26个字母和数字组成。
2、 RSA公私钥生成:
openssl genrsa -out rsa_private_key_2048.pem 2048 #生成rsa私钥,以X509编码,指定生成的密钥的位数: 2048(注:该步骤生成的私钥只为供第二步使用,并无实际用处)
openssl pkcs8 -topk8 -in rsa_private_key_2048.pem -out pkcs8_rsa_private_key_2048.pem -nocrypt #将上一步生成的rsa私钥转换成PKCS#8编码(注:该步骤生成的私钥构成实际密钥对的私钥)
openssl rsa -in rsa_private_key_2048.pem -out rsa_public_key_2048.pem -pubout #导出rsa公钥,以X509编码(注:该步骤生成的公钥构成实际密钥对的公钥)
其中openssl安装比较麻烦的,我就直接使用了二进制的exe。下载地址:http://gnuwin32.sourceforge.net/packages/openssl.htm
2 java中使用生成的公钥和私钥
产生的公私钥,其实是两个含有字符的文件,在Java中使用使用之前肯定需要转换成java.security.PrivateKey和java.security.PublicKey;
2.1 先以字符的形式读入:
InputStream is =CmbcTest.class.getClassLoader().getResourceAsStream(path);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String readLine = null;
while ((readLine = br.readLine()) != null) {
sb.append(readLine);
sb.append('\r');
}
2.2 转化为java对象:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(Base64.decodeBase64(sb.toString()));
pubKey = keyFactory.generatePublic(pubX509);
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(sb.toString()));
priKey = keyFactory.generatePrivate(priPKCS8);
2.3 使用RSA加解密(由于本人对于密码学的内部机制不熟,就从网上down了一下,仅供参考)
public abstract class CryptoUtil {
private static final String AES_ALG = "AES";
/**
* AES算法
*/
private static final String AES_ECB_PCK_ALG = "AES/ECB/PKCS5Padding";
/**
* 数字签名函数入口
*
* @param plainBytes
* 待签名明文字节数组
* @param privateKey
* 签名使用私钥
* @param signAlgorithm
* 签名算法
* @return 签名后的字节数组
*/
public static String digitalSign(String content, PrivateKey privateKey, String signAlgorithm, String charset) throws Exception {
Signature signature = Signature.getInstance(signAlgorithm);
signature.initSign(privateKey);
signature.update(content.getBytes(charset));
return new String(Base64.encodeBase64(signature.sign()), charset);
}
/**
* 验证数字签名函数入口
*
* @param plainBytes
* 待验签明文字节数组
* @param signBytes
* 待验签签名后字节数组
* @param publicKey
* 验签使用公钥
* @param signAlgorithm
* 签名算法
* @return 验签是否通过
*/
public static boolean verifyDigitalSign(byte[] plainBytes, String sign, PublicKey publicKey, String signAlgorithm, String charset) throws Exception {
byte[] signBytes = Base64.decodeBase64(sign.getBytes(charset));
Signature signature = Signature.getInstance(signAlgorithm);
signature.initVerify(publicKey);
signature.update(plainBytes);
return signature.verify(signBytes);
}
/**
* RSA加密
*
* @param plainBytes
* 明文字节数组
* @param publicKey
* 公钥
* @param keyLength
* 密钥bit长度
* @param reserveSize
* padding填充字节数,预留11字节
* @param cipherAlgorithm
* 加解密算法,一般为RSA/ECB/PKCS1Padding
* @return 加密后字节数组,不经base64编码
*/
public static String RSAEncrypt(String key, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm, String charset) throws Exception {
byte[] plainBytes = key.getBytes(charset);
int keyByteSize = keyLength / 8; // 密钥字节数
int encryptBlockSize = keyByteSize - reserveSize; // 加密块大小=密钥字节数-padding填充字节数
int nBlock = plainBytes.length / encryptBlockSize;// 计算分段加密的block数,向上取整
if ((plainBytes.length % encryptBlockSize) != 0) { // 余数非0,block数再加1
nBlock += 1;
}
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 输出buffer,大小为nBlock个keyByteSize
ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
// 分段加密
for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
int inputLen = plainBytes.length - offset;
if (inputLen > encryptBlockSize) {
inputLen = encryptBlockSize;
}
// 得到分段加密结果
byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
// 追加结果到输出buffer中
outbuf.write(encryptedBlock);
}
outbuf.flush();
outbuf.close();
return new String(Base64.encodeBase64(outbuf.toByteArray()), charset);
}
/**
* RSA解密
*
* @param encryptedBytes
* 加密后字节数组
* @param privateKey
* 私钥
* @param keyLength
* 密钥bit长度
* @param reserveSize
* padding填充字节数,预留11字节
* @param cipherAlgorithm
* 加解密算法,一般为RSA/ECB/PKCS1Padding
* @return 解密后字节数组,不经base64编码
*/
public static byte[] RSADecrypt(String encryptKey, PrivateKey privateKey, int keyLength, int reserveSize, String cipherAlgorithm, String charset) throws Exception {
byte[] encryptedBytes = Base64.decodeBase64(encryptKey.getBytes(charset));
int keyByteSize = keyLength / 8; // 密钥字节数
int decryptBlockSize = keyByteSize - reserveSize; // 解密块大小=密钥字节数-padding填充字节数
int nBlock = encryptedBytes.length / keyByteSize;// 计算分段解密的block数,理论上能整除
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 输出buffer,大小为nBlock个decryptBlockSize
ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlockSize);
// 分段解密
for (int offset = 0; offset < encryptedBytes.length; offset += keyByteSize) {
// block大小: decryptBlock 或 剩余字节数
int inputLen = encryptedBytes.length - offset;
if (inputLen > keyByteSize) {
inputLen = keyByteSize;
}
// 得到分段解密结果
byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLen);
// 追加结果到输出buffer中
outbuf.write(decryptedBlock);
}
outbuf.flush();
outbuf.close();
return outbuf.toByteArray();
}
/**
* AES加密
*
* @param plainBytes
* 明文字节数组
* @param keyBytes
* 密钥字节数组
* @param keyAlgorithm
* 密钥算法
* @param cipherAlgorithm
* 加解密算法
* @param IV
* 随机向量
* @return 加密后字节数组,不经base64编码
*/
public static String AESEncrypt(String content, String key, String charset) throws Exception {
byte[] keyBytes = key.getBytes(charset);
// AES密钥长度为128bit、192bit、256bit,默认为128bit
if (keyBytes.length % 8 != 0 || keyBytes.length < 16 || keyBytes.length > 32) {
throw new Exception("AES密钥长度不合法");
}
Cipher cipher = Cipher.getInstance(AES_ECB_PCK_ALG);
SecretKey secretKey = new SecretKeySpec(keyBytes, AES_ALG);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(content.getBytes(charset));
return new String(Base64.encodeBase64(encryptedBytes), charset);
}
/**
* AES解密
*
* @param encryptedBytes
* 密文字节数组,不经base64编码
* @param keyBytes
* 密钥字节数组
* @param keyAlgorithm
* 密钥算法
* @param cipherAlgorithm
* 加解密算法
* @param IV
* 随机向量
* @return 解密后字节数组
*/
public static byte[] AESDecrypt(String content, byte[] keyBytes, String charset) throws Exception {
// AES密钥长度为128bit、192bit、256bit,默认为128bit
if (keyBytes.length % 8 != 0 || keyBytes.length < 16 || keyBytes.length > 32) {
throw new Exception("AES密钥长度不合法");
}
Cipher cipher = Cipher.getInstance(AES_ECB_PCK_ALG);
SecretKey secretKey = new SecretKeySpec(keyBytes, AES_ALG);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(Base64.decodeBase64(content.getBytes(charset)));
}
}
3 使用生成RSA和AES
String key = generateLenString(16);
String encryptData = CryptoUtil.AESEncrypt(content, key, charset);
String signData = CryptoUtil.digitalSign(content, priKey, "SHA1WithRSA", charset);
String encrtptKey = CryptoUtil.RSAEncrypt(key, pubKey, 2048, 11, "RSA/ECB/PKCS1Padding", charset);
/**
* 生成指定长度的随机字符串
*
* @param length
* 指定字符串长度
* @return
*/
private static String generateLenString(int length) {
char[] cResult = new char[length];
int[] flag = { 0, 0, 0 }; // A-Z, a-z, 0-9
int i = 0;
while (flag[0] == 0 || flag[1] == 0 || flag[2] == 0 || i < length) {
i = i % length;
int f = (int) (Math.random() * 3 % 3);
if (f == 0)
cResult[i] = (char) ('A' + Math.random() * 26);
else if (f == 1)
cResult[i] = (char) ('a' + Math.random() * 26);
else
cResult[i] = (char) ('0' + Math.random() * 10);
flag[f] = 1;
i++;
}
return new String(cResult);
}
byte[] keyBytes = CryptoUtil.RSADecrypt(encryptKey, priKey, 2048, 11, "RSA/ECB/PKCS1Padding", charset);
byte[] dataBytes = CryptoUtil.AESDecrypt(encryptData, keyBytes, charset);
System.out.println("@@@"+new String(dataBytes,"utf-8"));
CryptoUtil.verifyDigitalSign(dataBytes, signData, pubKey, "SHA1WithRSA", charset)