[关闭]
@HUST-SuWB 2018-04-22T09:01:28.000000Z 字数 6246 阅读 475

对称加密&非对称加密

Finlabtech


定义

AES对称加密算法:客户端服务端使用相同的密钥进行加解密操作,因此密钥的安全性就很重要。
RSA非对称加密算法:客户端使用公钥加密,服务端使用对应的私钥进行解密,由于私钥只有服务端有,因此即便公钥泄露,第三方依然无法解密传输的信息。当然,反过来如果服务端使用私钥加密,那么客户端也可以使用公钥进行解密,此时客户端可以肯定信息是通过服务端发出来的,因此只有服务端握有私钥,这种思路可以用来进行身份校验。

实战

理论上,只要密钥在网络上传输就有泄露的风险,因此直接使用AES时有一定风险的,但如果全部用RSA又不太现实,首先RSA能加密的数据量比较小,其次RSA很慢,对于大数据量来说计算切割成很多段使用RSA加密也会过于耗时,相反,AES可以直接在硬件层面加速,效率就会高很多。
所以,在实际使用中一般的推荐做法是主体信息使用AES加密,但是AES的密钥通过RSA加密传输,这就保证了AES密钥的安全性。
由于项目需要与第三方进行接口交互,请求是直接走的http,所以对于接口的入参需要一定的加密处理,所以在经过了一番调研后确定了AES+RSA的加密方案,这里可以推荐一个RSA的实现示例,是我在网上找到的,具体链接见代码的说明。

  1. import org.apache.commons.codec.binary.Base64;
  2. import org.apache.commons.io.IOUtils;
  3. import javax.crypto.Cipher;
  4. import java.io.ByteArrayOutputStream;
  5. import java.security.*;
  6. import java.security.interfaces.RSAPrivateKey;
  7. import java.security.interfaces.RSAPublicKey;
  8. import java.security.spec.InvalidKeySpecException;
  9. import java.security.spec.PKCS8EncodedKeySpec;
  10. import java.security.spec.X509EncodedKeySpec;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. /**
  14. * @author: tianxie
  15. * @Description: https://blog.csdn.net/cz0217/article/details/78426733
  16. * @Date: 2018/3/29下午3:44
  17. */
  18. public class RSAUtils2 {
  19. public static final String CHARSET = "UTF-8";
  20. public static final String RSA_ALGORITHM = "RSA";
  21. public static Map<String, String> createKeys(int keySize) {
  22. //为RSA算法创建一个KeyPairGenerator对象
  23. KeyPairGenerator kpg;
  24. try{
  25. kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
  26. }catch(NoSuchAlgorithmException e){
  27. throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
  28. }
  29. //初始化KeyPairGenerator对象,密钥长度
  30. kpg.initialize(keySize);
  31. //生成密匙对
  32. KeyPair keyPair = kpg.generateKeyPair();
  33. //得到公钥
  34. Key publicKey = keyPair.getPublic();
  35. String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
  36. //得到私钥
  37. Key privateKey = keyPair.getPrivate();
  38. String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
  39. Map<String, String> keyPairMap = new HashMap<String, String>();
  40. keyPairMap.put("publicKey", publicKeyStr);
  41. keyPairMap.put("privateKey", privateKeyStr);
  42. return keyPairMap;
  43. }
  44. /**
  45. * 得到公钥
  46. * @param publicKey 密钥字符串(经过base64编码)
  47. * @throws Exception
  48. */
  49. public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
  50. //通过X509编码的Key指令获得公钥对象
  51. KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
  52. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
  53. RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
  54. return key;
  55. }
  56. /**
  57. * 得到私钥
  58. * @param privateKey 密钥字符串(经过base64编码)
  59. * @throws Exception
  60. */
  61. public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
  62. //通过PKCS#8编码的Key指令获得私钥对象
  63. KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
  64. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
  65. RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
  66. return key;
  67. }
  68. /**
  69. * 公钥加密
  70. * @param data
  71. * @param publicKey
  72. * @return
  73. */
  74. public static String publicEncrypt(String data, RSAPublicKey publicKey) {
  75. try{
  76. Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
  77. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  78. return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
  79. }catch(Exception e){
  80. throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
  81. }
  82. }
  83. /**
  84. * 私钥解密
  85. * @param data
  86. * @param privateKey
  87. * @return
  88. */
  89. public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
  90. try{
  91. Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
  92. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  93. return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
  94. }catch(Exception e){
  95. throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
  96. }
  97. }
  98. /**
  99. * 私钥加密
  100. * @param data
  101. * @param privateKey
  102. * @return
  103. */
  104. public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
  105. try{
  106. Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
  107. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
  108. return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
  109. }catch(Exception e){
  110. throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
  111. }
  112. }
  113. /**
  114. * 公钥解密
  115. * @param data
  116. * @param publicKey
  117. * @return
  118. */
  119. public static String publicDecrypt(String data, RSAPublicKey publicKey) {
  120. try{
  121. Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
  122. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  123. return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
  124. }catch(Exception e){
  125. throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
  126. }
  127. }
  128. private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize){
  129. int maxBlock = 0;
  130. if(opmode == Cipher.DECRYPT_MODE){
  131. maxBlock = keySize / 8;
  132. }else{
  133. maxBlock = keySize / 8 - 11;
  134. }
  135. ByteArrayOutputStream out = new ByteArrayOutputStream();
  136. int offSet = 0;
  137. byte[] buff;
  138. int i = 0;
  139. try{
  140. while(datas.length > offSet){
  141. if(datas.length-offSet > maxBlock){
  142. buff = cipher.doFinal(datas, offSet, maxBlock);
  143. }else{
  144. buff = cipher.doFinal(datas, offSet, datas.length-offSet);
  145. }
  146. out.write(buff, 0, buff.length);
  147. i++;
  148. offSet = i * maxBlock;
  149. }
  150. }catch(Exception e){
  151. throw new RuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常", e);
  152. }
  153. byte[] resultDatas = out.toByteArray();
  154. IOUtils.closeQuietly(out);
  155. return resultDatas;
  156. }
  157. }

调用示例如下:

  1. @Test
  2. public void test2() {
  3. Map<String, String> keyMap = RSAUtils2.createKeys(1024);
  4. String publicKey = keyMap.get("publicKey");
  5. String privateKey = keyMap.get("privateKey");
  6. System.out.println("公钥: \n\r" + publicKey);
  7. System.out.println("私钥: \n\r" + privateKey);
  8. System.out.println("公钥加密——私钥解密");
  9. String str = "站在大明门前守卫的禁卫军,事先没有接到\n" +
  10. "有关的命令,但看到大批盛装的官员来临,也就\n" +
  11. "以为确系举行大典,因而未加询问。进大明门即\n" +
  12. "为皇城。文武百官看到端门午门之前气氛平静,\n" +
  13. "城楼上下也无朝会的迹象,既无几案,站队点名\n" +
  14. "的御史和御前侍卫“大汉将军”也不见踪影,不免\n" +
  15. "心中揣测,互相询问:所谓午朝是否讹传?";
  16. System.out.println("\r明文:\r\n" + str);
  17. System.out.println("\r明文大小:\r\n" + str.getBytes().length);
  18. try {
  19. String encodedData = RSAUtils2.publicEncrypt(str, RSAUtils2.getPublicKey(publicKey));
  20. System.out.println("密文:\r\n" + encodedData);
  21. String decodedData = RSAUtils2.privateDecrypt(encodedData, RSAUtils2.getPrivateKey(privateKey));
  22. System.out.println("解密后文字: \r\n" + decodedData);
  23. } catch (Exception e) {}
  24. }

扩展

在调用相关加密算法的时候,除了AES和RSA之外你会看到很多文章都会提到以MD5为代表的摘要算法。理论上MD5是不可逆的,所以就不存在破解的说法,MD5也广泛的应用在密码加密领域。很多系统的用户密码都是使用的MD5加密,但由于彩虹表的存在,常用密码的MD5密文其实都可以通过查表查出来,所以单纯的使用MD5并不是一个保险的做法,因此现在普遍都是使用的MD5(msg+salt),甚至设定两个salt进行两层MD5。
同时,还有一个名词也很常见,叫base64,需要明确的是base64并不是加密算法,所以它是无法加密信息的,它其实是一种编码,存在的意义是为了方便信息传输,具体介绍可以参考知乎的一篇文章:如何用通俗易懂的语言解释base64

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注