RSA非对称性前端加密后端解密
参考:http://netsecurity.51cto.com/art/201108/287971.htm
对称加密和非对称加密。
对称加密中加密和解密都是用同一个密钥,如 AES/DES。只要其中一个人的密钥泄露了,那么整体加密信息都将被破解。
非对称加密是每个人生成一个密钥对即私钥和公钥,使用私钥加密的信息,只有对应的公钥才能解密,反之,使用公钥加密的信息,只能由对应的私钥才能解密。一定要保护好私钥。从性能上来说,非对称加密相对于对称加密要慢很多,所以一般只用于少量数据的加密,如登录密码。 RSA 就是一种非对称加密算法。
数字签名
数字签名的意义在于,对传输过来的数据进行校验,确保数据在传输工程中不被修改。明文信息在传输之前采用hash算法获取摘要然后进行加密即为数字签名。
数字证书
数字证书的意义在于,确保公钥没被篡改。数字证书需要由权威的公司来提供,用户通过CA的公钥解密后得到发送人的公钥。数字证书主要包括:证书的发布机构,证书的有效期,公钥,证书所有者,签名所使用的的算法。如果没有数字证书,黑客制作了一个密钥对,将公钥发给用户,用户不加判断将密文发给黑客,岂不是同样有风险?
数据交互流程分析
甲方传递信息给乙方,首先生成密钥对,对明文信息进行哈希运算得到信息摘要,然后用私钥对信息摘要进行加密得到数字签名,并将其附在信息上,一起发出。
乙方接收到密文后,用公钥对数字签名进行解密,得到信息摘要,用相同的哈希算法读取密文,比对信息是否是甲方发出,由此判断密文是否被人修改过。
如果出现丙方,偷偷用自己的公钥换掉了甲方的公钥,这样乙方在收到信息后就无法确定信息是不是甲方发出的。所以还需要判断签名是不是甲方的签名。
证书中心为公钥做认证,证书中心会对甲方的公钥和一些相关信息加密,生成数字证书。这样甲方在给乙方发送信息时,只需在签名的同时,再附上数字证书就可以了。
乙方在收到信息后,使用CA的公钥解开数字证书,就可以拿到甲方的公钥,然后就证明数字签名是不是甲方的。
https就是应用数字证书的一个示例。
另一个流程
1、user向server发送一个通信请求
user->server:你好
2、server向user发送自己的数字证书。证书中有一个公钥用来加密信息,私钥由server持有
server->user:你好,我是server,这里是我的数字证书
3、user收到server的证书后,它会去验证这个数字证书到底是不是server的,数字证书有没有什么问题,数字证书如果检查没有问题,就说明数字证书中的公钥确实是server的。检查数字证书后,user会发送一个随机的字符串给server用私钥去加密(检查数字证书,后文介绍)
user->server:向我证明你就是server,这是一个随机字符串
4、server把加密的结果返回给user。
server->user:{一个随机字符串(信息摘要加密)}(用私钥进行RSA加密)
5、user用公钥解密这个返回结果,如果解密结果与之前生成的随机字符串一致,那说明对方确实是私钥的持有者,或者说对方确实是server。 验证server的身份后,user生成一个对称加密算法和密钥,用于后面的通信的加密和解密。这个对称加密算法和密钥,user会用公钥加密后发送给server,别人截获了也没用,因为只有server手中有可以解密的私钥。这样,后面server和user就都可以用对称加密算法来加密和解密通信内容了。
server->user:{OK,已经收到你发来的对称加密算法和密钥!有什么可以帮到你的?}(用密钥进行对称加密)
user->server:{我的用户名是test,密码是123321,我要登录}(用密钥进行对称加密)
server->user:{你好,你已登录成功}(用密钥进行对称加密)
/** * RSA 工具类。提供加密,解密,生成密钥对等方法。 * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。 * */ public class RSAUtils { public static final String WEB_MODULUS = "abcb0c959f370bcaae9dff448826ccec64616debac81b220d3302296c1f7cc845b83c449c0ae35bfe49869d0924466de5dfdf416998516e95bd4ec0fdfb825eb3c1b2864dad0786f35675f5cf63fbea271cb1079330ef84dab8941d140aa57f93c853e0518051e5751e78512667d8047b82e7e3adae0bf3dcb95c6a7852763db"; public static final String WEB_PRIVATEEXPONENT = "528eb47fd3ab3348beb8c0d44f94dc380bb916266e190c321b6927fa1f8d60dda273c389398db0eb3c3cb1ac2ec049e6247cd6b70e158d200c6ef8bc42110c5d103cd5e5c51ce265f20dbd967231a505b5f179701e7b5e5e6d59530a9f291464c3101a18e7755fc9dfbefc091c52e51528d47a7b87a6f2349fb8c2a9ffc5f4d9"; /** * * 生成密钥对 * @return KeyPair * @throws Exception */ public static KeyPair generateKeyPair(File cfgFile) throws Exception { try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); final int KEY_SIZE = 1024;// 没什么好说的了,这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低 keyPairGen.initialize(KEY_SIZE, new SecureRandom()); KeyPair keyPair = keyPairGen.generateKeyPair(); saveKeyPair(keyPair, cfgFile); return keyPair; } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * getKeyPair * @param cfgFile * @return * @throws Exception */ public static KeyPair getKeyPair(File cfgFile) throws Exception { FileInputStream fis = new FileInputStream(cfgFile); ObjectInputStream oos = new ObjectInputStream(fis); KeyPair kp = (KeyPair) oos.readObject(); oos.close(); fis.close(); return kp; } /** * saveKeyPair * @param kp * @param cfgFile * @throws Exception */ public static void saveKeyPair(KeyPair kp, File cfgFile) throws Exception { FileOutputStream fos = new FileOutputStream(cfgFile); ObjectOutputStream oos = new ObjectOutputStream(fos); //生成密钥 oos.writeObject(kp); oos.close(); fos.close(); } /** * * 生成公钥 * @param modulus * @param publicExponent * @return RSAPublicKey * @throws Exception */ public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) throws Exception { KeyFactory keyFac = null; try { keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); } catch (NoSuchAlgorithmException ex) { throw new Exception(ex.getMessage()); } RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent)); try { return (RSAPublicKey) keyFac.generatePublic(pubKeySpec); } catch (InvalidKeySpecException ex) { throw new Exception(ex.getMessage()); } } /** * * 生成私钥 * @param modulus * @param privateExponent * @return RSAPrivateKey * @throws Exception */ public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) throws Exception { KeyFactory keyFac = null; try { keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); } catch (NoSuchAlgorithmException ex) { throw new Exception(ex.getMessage()); } RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent)); try { return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec); } catch (InvalidKeySpecException ex) { throw new Exception(ex.getMessage()); } } /** * * 加密 * @param pk 加密的密钥 * @param data 待加密的明文数据 * @return 加密后的数据 * @throws Exception */ public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception { try { Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, pk); int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024 // 加密块大小为127 // byte,加密后为128个byte;因此共有2个加密块,第一个127 // byte第二个为1个byte int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小 int leavedSize = data.length % blockSize; int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize; byte[] raw = new byte[outputSize * blocksSize]; int i = 0; while (data.length - i * blockSize > 0) { if (data.length - i * blockSize > blockSize) { cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize); } else { cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize); } // 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到 // ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了 // OutputSize所以只好用dofinal方法。 i++; } return raw; } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * 解密 * @param pk 解密的密钥 * @param raw 已经加密的数据 * @return 解密后的明文 * @throws Exception */ @SuppressWarnings("static-access") public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception { try { Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); cipher.init(cipher.DECRYPT_MODE, pk); int blockSize = cipher.getBlockSize(); ByteArrayOutputStream bout = new ByteArrayOutputStream(64); int j = 0; while (raw.length - j * blockSize > 0) { bout.write(cipher.doFinal(raw, j * blockSize, blockSize)); j++; } return bout.toByteArray(); } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * hexStringToBytes * @param hexString * @return */ public static byte[] hexStringToBytes(String hexString) { if (null == hexString || "".equals(hexString)) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d; } private static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { String result = "3bcf5f398f5017b66d3d8df2db3d88c9be06f8397b28b6f3b6e430d5abd84b3644d601b477c4292b53d4c28f17b9864803bdd8c93a2c2a4e9b277f89c5c648de96da9cc91d1cdab3e3adcdb837f0f0fbd6be8b6c2e36d735f8aa1674439b11ed3520303cef5c2c886363c63db70b844c95c8284b12bdb48b43ade84b707627a2"; System.out.println("原文加密后为:"); System.out.println(result); byte[] en_result = hexStringToBytes(result);//new BigInteger(result, 16).toByteArray(); String modulus = WEB_MODULUS; String privateExponent = WEB_PRIVATEEXPONENT; RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) RSAUtils.generateRSAPrivateKey(new BigInteger(modulus, 16).toByteArray(), new BigInteger(privateExponent, 16).toByteArray()); byte[] de_result = RSAUtils.decrypt(rsaPrivateKey, en_result); System.out.println("还原密文:"); StringBuffer sb = new StringBuffer(); sb.append(new String(de_result)); System.out.println(sb.reverse().toString().substring(13)); } }
<!DOCTYPE html> <html> <head> <!-- <base href=""></base> --> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script src="jquery-1.8.3.js"></script> <script src="jsencrypt.min.js"></script> <script src="Barrett.js"></script> <script src="BigInt.js"></script> <script type="text/javascript"> function webencryptAES(str){ if(!str) return ""; setMaxDigits(130); var PublicExponent ="10001"; var modulus ="abcb0c959f370bcaae9dff448826ccec64616debac81b220d3302296c1f7cc845b83c449c0ae35bfe49869d0924466de5dfdf416998516e95bd4ec0fdfb825eb3c1b2864dad0786f35675f5cf63fbea271cb1079330ef84dab8941d140aa57f93c853e0518051e5751e78512667d8047b82e7e3adae0bf3dcb95c6a7852763db"; var key = new RSAKeyPair(PublicExponent,"",modulus); var ramdomTxt= String(new Date().getTime()); if(ramdomTxt.length>8){ ramdomTxt= ramdomTxt.substring(ramdomTxt.length-8); }else if(ramdomTxt.length<8){ var lStr=""; for(var i=0;i<8-ramdomTxt.length;i++){ lStr+="0"; } ramdomTxt = lStr+ramdomTxt; } ramdomTxt+=String(Math.ceil(Math.random()*100000)); return encryptedString(key,ramdomTxt+String(str)); } </script> <script type="text/javascript"> $(function(){ $("#getPwtBtn").click(function(){ var pwdText = $("#pwdText").val(); var test = webencryptAES(pwdText); $("#resultPwd").text(test); }); }); </script> </body> <body> <div id="" class=""> <label>密码:</label> <input id="pwdText" type="text"></input> <button id="getPwtBtn">获取密文</button> </div> <div id="" class="" style="width:60%"> <textfield id="resultPwd"></textfield> </div> </body> </html>
1、通过代码能看出来,公钥长度明显小于私钥
2、遵循:公钥加密-私钥解密,私钥加密-公钥解密的原则
3、公钥和私钥肯定是完全不同
- 上一篇: 简单粗暴的前端加解密方法
- 下一篇: jq 获取dom对象this和event.target