RSA加密
RSA加密的原理:
https://www.cnblogs.com/coolYuan/p/9168284.html
其实知不知道RSA加密的原理都不太影响开发,无非是前端页面输入用户名密码后,在发送请求到后台去的时候,把用户名密码的明文字符串加密成密文传递到后台,这样别人截获请求参数的时候,截获到的只能是看不懂的密文,后台收到密文后,进行解密,解密后用明文做明文原本该做的逻辑判断和处理。
简单说就是js加密,Java解密。
js加密需要一个js的RSA加密库 jsencrypt.min.js
java解密需要一个RSA加密解密的jar包 bcprov-jdk15-145.jar
js代码:
<!-- 用户名密码加密 --> <script type="text/javascript" src="/login/js/jsencrypt.min.js"></script> <!-- remote --> <script type="text/javascript" src="/resource/remoting/resource/remote.js"></script> <script type="text/javascript" src="/login/js/signInAction.js"></script> <h:outputScript library="seam" name="seam.rescript.js" /> <script> var publicKey = ""; var encrypt = new JSEncrypt(); function getPublicKey() { Seam.Component.getInstance("signInAction").getKey(function(data){ console.log(data); publicKey = data; encrypt.setPublicKey(publicKey); }, excHandler); } function encryptionUserName() { var username = $('.loguserName').val(); username = encrypt.encrypt(username); Seam.Component.getInstance("signInAction").setUserName(username, function(){}, excHandler); } function encryptionUserPwd() { var password = $('.logpassword').val(); password = encrypt.encrypt(password); Seam.Component.getInstance("signInAction").setUserPwd(password, function(){}, excHandler); } j(function(){ getPublicKey(); }); </script>
getPublicKey 获取公钥,encrypt.setPublicKey 这个是设置公钥, encrypt.encrypt 这个是加密方法, Seam.Component.getInstance 这个是平台框架的类似Ajax的方法,用于js请求后台。
Java 加密解密工具类代码:
package com.seam.mango.utils; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.security.*; import java.security.interfaces.RSAPublicKey; import java.security.spec.X509EncodedKeySpec; /** * 用到的jar包:bcprov-jdk15-145.jar*/ public class RSAUtils {private static final KeyPair keyPair = initKey(); /** * 计算公钥 */ private static KeyPair initKey() { try { Provider provider =new org.bouncycastle.jce.provider.BouncyCastleProvider(); Security.addProvider(provider); SecureRandom random = new SecureRandom(); KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider); generator.initialize(1024, random); return generator.generateKeyPair(); } catch(Exception e) { throw new RuntimeException(e); } } /** * 加密 */ public static String encrypt( String str, String publicKey ) throws Exception{ //base64编码的公钥 byte[] decoded = Base64.decodeBase64(publicKey.getBytes()); RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); //RSA加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); byte[] outStr = Base64.encodeBase64(cipher.doFinal(str.getBytes("UTF-8"))); return new String(outStr); } /** * 获取公钥 */ public static String generateBase64PublicKey() { PublicKey publicKey = (RSAPublicKey)keyPair.getPublic(); return new String(Base64.encodeBase64(publicKey.getEncoded())); } /** * 解密 */ public static String decryptBase64(String string) { return new String(decrypt(Base64.decodeBase64(string.getBytes()))); } private static byte[] decrypt(byte[] byteArray) { try { Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider(); Security.addProvider(provider); Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" /*"RSA/None/PKCS1Padding"*/ /*"RSA/None/NoPadding"*/ /*"RSA/ECB/NoPadding"*/ , provider); PrivateKey privateKey = keyPair.getPrivate(); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] plainText = cipher.doFinal(byteArray); return plainText; } catch(Exception e) { throw new RuntimeException(e); } } }
java原先的登录方法只需要接收加密的字符串调用解密的方法就可以了,原先的逻辑不用动:
/** * 前端获取公钥 */ @WebRemote public String getKey(){ return RSAUtils.generateBase64PublicKey(); } /** * 接收前端用户名密文 */ @WebRemote public void setUserName(String username){ credentials.setUsername(username); System.out.println("==================== username: " + username + " ====================="); } /** * 接收前端密码密文 */ @WebRemote public void setUserPwd(String pwd){ credentials.setPassword(pwd); System.out.println("==================== pwd: " + pwd + " ====================="); }
登录方法里加入解密过程:
credentials.setUsername(RSAUtils.decryptBase64(credentials.getUsername()));
credentials.setPassword(RSAUtils.decryptBase64(credentials.getPassword()));
上面说的这些其实都不是难点,难的是环境的配置,因为没有人告诉你该怎么配环境,只能看代码跑起来的时候报什么错,根据报错内容去搜索解决方案,大致遇到的就是下面两个异常:
1、unknown block type
这个异常百度后,大致觉得是因为jdk在调用加密解密有关的jar包时,需要安全认证
我不明白,Security.addProvider(provider); 这句话不起作用的吗?
手动加认证的方式:
修改 jdk1.8.0_60_x64\jre\lib\security\java.security
添加一个provider
security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
将jar包 bcprov-jdk15-145.jar 放入 jdk1.8.0_60_x64\jre\lib\ext 目录
2、Cannot verify jar
我对这个异常的大致理解是 jboss 运行时,对加密解密也需要安全认证
需要修改jboss的配置文件,步骤如下:
在 jboss-eap-6.2\modules\org 路径下添加 bouncycastle 文件夹,里面再添加 main 文件夹
把jar包 bcprov-jdk15-145.jar 放入 jboss-eap-6.2\modules\org\bouncycastle\main 目录
并再 jboss-eap-6.2\modules\org\bouncycastle\main 目录下添加 module.xml 文件,这个文件可以仿照 modules 文件夹里的其他 module.xml 文件,内容如下
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.1" name="org.bouncycastle"> <resources> <resource-root path="bcprov-jdk15-145.jar"/> </resources> <dependencies> <module name="javax.api" slot="main" export="true"/> </dependencies> </module>
最后一步,找到 jboss-eap-6.2\standalone\configuration\standalone.xml 配置文件, 并找到这一行:<subsystem xmlns="urn:jboss:domain:ee:1.1">
添加下面的配置:
<global-modules> <module name="org.bouncycastle" slot="main"/> </global-modules>
到此,这个看似小菜却又并不小菜的功能搞定!