- 保存密钥
对称密钥一般使用二进制保存
SecretKey desKey = KeyGenerator.getInstance(“DESede”).generateKey();
// 二进制密钥
byte[] bkey = desKey.getEncoded();
// 转成十六进制
String keyStr = Hex.byteCoverToString(bkey);
- 读取密钥
可以自己生成随机字符串转成byte数组生成密钥,注意byte长度不能小于24位。
// bkey是byte数组
DESedeKeySpec keySpec = new DESedeKeySpec(bkey);
// 读取AES密钥
SecretKey srtKey = SecretKeyFactory.getInstance(“AES”).generateSecret(keySpec);
Hex是我自己写的工具类。byte数组和十六进制互转的方法有很多,篇幅有限就不贴出来了。可以引入 Apache Commons Codec 工具类,提供Hex,Base64等常用方法。
非对称加密
非对称加密使用密钥对进行加密。一般使用私密加密,公钥解密,也可以反过来用。但自己加密的数据自己不能解,只能依靠对方解密,可以很好的防止单方面密钥泄露。常用的加密算法有:RSA,DSA。
- 生成密钥对
/**
* 可以传RSA或DSA算法
*/
KeyPair keyPair = KeyPairGenerator.getInstance(“RSA”).generateKeyPair();
// 私钥
PrivateKey prvKey = keyPair.getPrivate();
// 公钥
PublicKey pubKey = keyPair.getPublic();
- 保存密钥
非对称密钥一般使用Base64编码保存。
// 私钥串
String prvKeyStr = Base64.getEncoder().encodeToString(prvKey.getEncoded());
// 公钥串
String pubKeyStr = Base64.getEncoder().encodeToString(pubKey.getEncoded());
非对称加密对加密内容长度有限制,不能超过192位。虽然可以使用加密数据数流来突破限制,但由于非对称加密效率不如对称加密,非对称加密一般用来加密对称密钥。
- 读取密钥
// 从二进制中读取私钥
byte[] bPrvKey = Base64.getDecoder().decode(prvKeyStr);
PKCS8EncodedKeySpec rsaKeySpec = new PKCS8EncodedKeySpec(bPrvKey);
PrivateKey prvKey = KeyFactory.getInstance(“RSA”).generatePrivate(rsaKeySpec);
// 从二进制中读取公钥
byte[] bPubKey = Base64.getDecoder().decode(pubKeyStr);
X509EncodedKeySpec rsaKeySpec1 = new X509EncodedKeySpec(bPubKey);
PublicKey pubKey = KeyFactory.getInstance(“RSA”).generatePublic(rsaKeySpec1);
我们在接入其他系统中,有时不是给密钥串,而是一个CA证书或PFX密钥文件。HTTS网站就是使用非对称加密的,网站会把CA证书挂在上面让浏览器下载,和服务器进行加解密交互。
- 从CA证书中读取公钥
// keyFilePath是证书文件
try (InputStream fin = new FileInputStream(keyFilePath)) {
CertificateFactory f = CertificateFactory.getInstance(“X.509”);
X509Certificate certificate = (X509Certificate) f.generateCertificate(fin);
PublicKey pubKey = certificate.getPublicKey();
} catch (IOException | CertificateException e) {
e.printStackTrace();
}
- 从PFX文件中读取密钥
根据密钥存储类型,下面方可以读取pfx或jks类型的密钥文件。
try (InputStream is = new FileInputStream(keyFilePath)) {
// pkcs12或jks
KeyStore store = KeyStore.getInstance(“pkcs12”);
// 密码
store.load(is, “密码”.toCharArray());
// 获取所有密钥别名列表
Enumeration e = store.aliases();
// 如果有,读取第一个密钥
if (e.hasMoreElements()) {
String alias = e.nextElement();
// 私钥
PrivateKey prvKey = (PrivateKey) store.getKey(alias, password.toCharArray());
// 公钥
PublicKey pubKey = store.getCertificate(alias).getPublicKey();
// 创建密钥对
KeyPair keyPare = new KeyPair(pubKey, prvKey);
}
} catch (IOException | KeyStoreException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
e.printStackTrace();
}
密钥文件是用来存储密钥的仓库。Java中KeyTool工具可以创建jks密钥文件。jks密钥文件中可以存储多个密钥。如果有多个密钥文件,可以根据别名来读取指定密钥。
加密扩展包——JCE
使用JDK8,读取PFX密钥也许会遇到:java.security.InvalidKeyException:illegal Key Size
这是因为jdk sercurity的jar包限制,只支持128bit的密钥。jar包如下:
$JAVA_HOME/lib/security/local_policy.jar
$JAVA_HOME/jre/lib/security/US_export_policy.jar
到Oracle官网下载JDK对应的加密扩展包(JCE)。以下是JDK8的JCE
JCE Unlimited Strength Jurisdiction Policy Files for JDK/JRE 8 Download
把下载后的文件解压,找到 local_policy.jar 和 US_export_policy.jar
覆盖jdk目录下, $JAVA_HOME/jre/lib/security
和 $JAVA_HOME/lib/security
下的文件。(有的版本JDK可能没有 $JAVA_HOME/lib/security 目录,则忽略)
再运行代码,就能正常读取256bit的密钥。
加密&解密
用上面生成的密钥(对称/非对密)加密或解密
// 密文
String ciphertext = null;
{
String plaintext = “加密内容”;
// 密钥算法,RSA/DAS/AESDESed
Cipher cipher = Cipher.getInstance(“RSA”);
// 设置加密模式
cipher.init(Cipher.ENCRYPT_MODE, prvKey);
// 加密后的byte数组
byte[] cipherbyte = cipher.doFinal(plaintext.getBytes());
// 转成base64
ciphertext = Base64.getEncoder().encodeToString(cipherbyte);
logger.info(ciphertext);
}
{
// 密钥算法,RSA/DAS/AESDESed
Cipher cipher = Cipher.getInstance(“RSA”);
// 设置解密模式
cipher.init(Cipher.DECRYPT_MODE, pubKey);
// base64解码获得密文byte数组
byte[] cipherbyte = Base64.getDecoder().decode(ciphertext);
// 解密后的byte数组
byte[] plainbyte = cipher.doFinal(cipherbyte);
// byte数组转字符串
String plaintext = new String(plainbyte);
logger.info(plaintext);
}
签名算法
保证数据安全,仅靠加密还不够。如果密钥被泄漏,攻击者可以伪靠数据,使用密钥加密后给服务器发送信息。所以还需要对数据签名,为安全增加一道屏障。常用的签名算法有:MD5,SHA。非对称加密的公/私钥也可以对数据签名。
- MD5或SHA签名
// 可传入MD5或SHA算法
MessageDigest md = MessageDigest.getInstance(“MD5”);
// 对str数据签名
byte[] bSign = md.digest(str.getBytes());
// 转成十六进制签名串
String signStr = Hex.byteCoverToString(bSign);
- 公/私密钥签名
/**
* 签名
* @param key
最后
整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣,
signStr = Hex.byteCoverToString(bSign);
- 公/私密钥签名
/**
* 签名
* @param key
最后
整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
[外链图片转存中…(img-6wr6DPW3-1714312006186)]
[外链图片转存中…(img-Fps9BWfe-1714312006187)]
其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣,