// 私钥串
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
* @param algorithm
* @param data
* @return
*/
public static byte[] sign(PrivateKey key, String algorithm, byte[] data) {
try {
Signature signature = Signature.getInstance(algorithm);
signature.initSign(key);
signature.update(data);
return signature.sign();
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
throw new CommonException(e);
}
}
/**
* 验签
* @param key
* @param algorithm
* @param sign
* @param data
* @return
*/
public static boolean verify(PublicKey key, String algorithm, byte[] sign, byte[] data) {
try {
Signature signature = Signature.getInstance(algorithm);
signature.initVerify(key);
signature.update(data);
return signature.verify(sign);
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
throw new CommonException(e);
}
}
设计规范示例
- 示例一
第一次接触Java加密是12年做银联直连支付插件,后台加密规范是银联出的,对称加密与非对称加密都用上了。如下:
协议版本|加密密钥|报文密文|报文签名
协议版本号一般是固定的,如:1.0。
加密密钥是动态生成的对称密钥,每一次请求都会生成,用来加密报文。加密密钥用自己的私钥加密,Base64编码。
报文密文是用动态生成的加密密钥加密后Base64编码。
报文签名是按约将报文明文参数排序,进行MD5计算。
- 示例二
因工作需内容,接入过几十加支付系统,他们的加触密都大同小异。示例一中银联的那套加密方案后面看到的不多。很多公司用的对称密钥+签名。
报文(key1=value1&k2=value2&sign=value)
先对报文进行规则排序(一般是key的ascii码升序或降序),再用事先约定好的对密密钥(十六进制字符串)拼接在后面,进行MD5计算。
也有的是使用RSA密钥签名验签。
跨语言加解密
Java原生API中加密算法提供者是SUN或Oracle。使用同相的Java代码,用Android加密,后台解密是不通过的。在Android上,算法要用 RSA/ECB/PKCS1Padding
,才能对应Oracle JDK的RSA。主要是因为非对称加密的默认填充方式不一样。
同样,用PHP或.NET语言和Java交互,也会出现不能解密的情况。所以有些系统设计上,只用了签名和对称加密,配合https在传输上保证内容加密。虽然方便,但没实际解决问题。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
结尾
这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。
由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
om: 33%;" />
结尾
[外链图片转存中…(img-kHjOn3H0-1713110831108)]
这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。
由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!