概述

RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困 难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。关于RSA其它需要了解的知识,参考维基百科:http://zh.wikipedia.org/zh-cn/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95

在项目开发中对于一些比较敏感的信息需要对其进行加密处理,我们就可以使用RSA这种非对称加密算法来对数据进行加密处理。

使用

秘钥对的生成

1、我们可以在代码里随机生成密钥对

[java] view plaincopy
  1. /**
  2. * 随机生成RSA密钥对
  3. *
  4. * @param keyLength
  5. *            密钥长度,范围:512~2048<br>
  6. *            一般1024
  7. * @return
  8. */
  9. public static KeyPair generateRSAKeyPair(int keyLength)
  10. {
  11. try
  12. {
  13. KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
  14. kpg.initialize(keyLength);
  15. return kpg.genKeyPair();
  16. } catch (NoSuchAlgorithmException e)
  17. {
  18. e.printStackTrace();
  19. return null;
  20. }
  21. }

通过这方法我们得到KeyPair后就可以调用 keyPair.getPrivate() 和 pair.getPublic()得到 私钥 和 公钥了。不过实际我们一般是使用2的方法把生成的密钥对保存起来,密钥自己保存,比如服务端,把公钥给客户端(Android)用于加密需要发送的数据,最后服务端根据私钥吧客户端加密的信息解密再处理。

2、通过OpenSSl工具生成密钥对

OpenSSl工具下载:OpenSSl工具 (64位的也可使用)使用OpenSSl工具生成密钥对的过程如下:

首先双击打开bin文件夹下的openssl.exe,打开之后是一个命令行窗口:

然后通过如下命令生成私钥:

[plain] view plaincopy
  1. genrsa -out rsa_private_key.pem 1024

这条命令是让openssl随机生成了一份私钥,加密长度是1024位, 密钥长度,范围:512~2048。执行完命令后就可在bin文件夹下看到rsa_private_key.pem文件了。

用文本类工具打开可看到里面的内容:

[plain] view plaincopy
  1. -----BEGIN RSA PRIVATE KEY-----
  2. MIICXQIBAAKBgQCfRTdcPIH10gT9f31rQuIInLwe7fl2dtEJ93gTmjE9c2H+kLVE
  3. NWgECiJVQ5sonQNfwToMKdO0b3Olf4pgBKeLThraz/L3nYJYlbqjHC3jTjUnZc0l
  4. uumpXGsox62+PuSGBlfb8zJO6hix4GV/vhyQVCpG9aYqgE7zyTRZYX9byQIDAQAB
  5. AoGAO9+sYRtKC9xJDfcocfMxv+UT/1ic6EDgcqu6Uzwq+Jvwod9KlXqyQJqCr6T7
  6. pjfodc3RAZOTx4gCZJverBvz053RH5GawCdocEgaqbXAAWJOhA+9IEU0NUud7ckF
  7. yDko0QXLoGP9tanrMEt5zMqt8QxDyl6Xcij3mk8rivOgBJECQQDNTO6dZX8xCozc
  8. Ne0gzC53Gv/KQXANBBHMr7WkKUb2i5+tXkEJ5z3abx2ppEQXDr4AgJH8Gtbm6K7t
  9. EHV4ov4FAkEAxppD/iiT1/SVQq20be8CsiHpsjTPiestWQWdm1Qn/Y2nAkGkpCFp
  10. yEdUvVDPtQhRN9EqNggNAnwg5kMvsuwN9QJAfHBhQe4/hk5Kyz+0l+irUW6AFOxN
  11. KtaIo3TtuK98X/yJsOAstAACMeCgLi9vRjqdWFiWJCVwlU38mZ0cVx8UsQJBALzt
  12. M5Er+LiPKw5rQCD0JZRfPnkQU/3XgyQUe4Gv5PsHLcCvwXeBcafcc3hEz9JfPyPi
  13. Dk2oCvg6LPHfKBkFBaECQQCODcKX6DBWiyVxmPaJOOcF63KpCYDPkjeovIUHro1x
  14. ElR2GrQCC/9Q4C4vruOhBQ+vX8NMPnO6NBy5TLGDwMyc
  15. -----END RSA PRIVATE KEY-----

这里面的内容是标准的ASCII字符,中间的一大串字符就是私钥数据了。

然后通过如下命令生成公钥:

[plain] view plaincopy
  1. rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout


打开文件看下里面的内容:

[plain] view plaincopy
  1. -----BEGIN PUBLIC KEY-----
  2. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfRTdcPIH10gT9f31rQuIInLwe
  3. 7fl2dtEJ93gTmjE9c2H+kLVENWgECiJVQ5sonQNfwToMKdO0b3Olf4pgBKeLThra
  4. z/L3nYJYlbqjHC3jTjUnZc0luumpXGsox62+PuSGBlfb8zJO6hix4GV/vhyQVCpG
  5. 9aYqgE7zyTRZYX9byQIDAQAB
  6. -----END PUBLIC KEY-----

可以看到是跟私钥的文件类似的。

这样密钥就基本生成了,不过这样密钥对的私钥是无法在代码中直接使用的,要想使用它需要借助RSAPrivateKeyStructure这个类,java是不自带的。所以为了方便使用,我们需要对私钥进行PKCS#8编码,命令如下:

[plain] view plaincopy
  1. pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt


这条命令的结果依然是在bin文件夹生成了pkcs8_rsa_private_key.pem文件,打开内容如下:

[plain] view plaincopy
  1. -----BEGIN PRIVATE KEY-----
  2. MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJ9FN1w8gfXSBP1/
  3. fWtC4gicvB7t+XZ20Qn3eBOaMT1zYf6QtUQ1aAQKIlVDmyidA1/BOgwp07Rvc6V/
  4. imAEp4tOGtrP8vedgliVuqMcLeNONSdlzSW66alcayjHrb4+5IYGV9vzMk7qGLHg
  5. ZX++HJBUKkb1piqATvPJNFlhf1vJAgMBAAECgYA736xhG0oL3EkN9yhx8zG/5RP/
  6. WJzoQOByq7pTPCr4m/Ch30qVerJAmoKvpPumN+h1zdEBk5PHiAJkm96sG/PTndEf
  7. kZrAJ2hwSBqptcABYk6ED70gRTQ1S53tyQXIOSjRBcugY/21qeswS3nMyq3xDEPK
  8. XpdyKPeaTyuK86AEkQJBAM1M7p1lfzEKjNw17SDMLnca/8pBcA0EEcyvtaQpRvaL
  9. n61eQQnnPdpvHamkRBcOvgCAkfwa1uboru0QdXii/gUCQQDGmkP+KJPX9JVCrbRt
  10. 7wKyIemyNM+J6y1ZBZ2bVCf9jacCQaSkIWnIR1S9UM+1CFE30So2CA0CfCDmQy+y
  11. 7A31AkB8cGFB7j+GTkrLP7SX6KtRboAU7E0q1oijdO24r3xf/Imw4Cy0AAIx4KAu
  12. L29GOp1YWJYkJXCVTfyZnRxXHxSxAkEAvO0zkSv4uI8rDmtAIPQllF8+eRBT/deD
  13. JBR7ga/k+wctwK/Bd4Fxp9xzeETP0l8/I+IOTagK+Dos8d8oGQUFoQJBAI4Nwpfo
  14. MFaLJXGY9ok45wXrcqkJgM+SN6i8hQeujXESVHYatAIL/1DgLi+u46EFD69fw0w+
  15. c7o0HLlMsYPAzJw=
  16. -----END PRIVATE KEY-----

可以看到中间的私钥内容有所变化了,这样的私钥我们在代码里就方便使用了。

以上的密钥文件使用时需要注意吧头和尾的字符串去掉,我们只取中间的内容。


代码中的使用

首先我们需要封装写个RSA的工具类,方便加密解密的操作。

[java] view plaincopy
  1. package com.example.rsa;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.InputStreamReader;
  6. import java.math.BigInteger;
  7. import java.security.KeyFactory;
  8. import java.security.KeyPair;
  9. import java.security.KeyPairGenerator;
  10. import java.security.NoSuchAlgorithmException;
  11. import java.security.PrivateKey;
  12. import java.security.PublicKey;
  13. import java.security.interfaces.RSAPrivateKey;
  14. import java.security.interfaces.RSAPublicKey;
  15. import java.security.spec.InvalidKeySpecException;
  16. import java.security.spec.PKCS8EncodedKeySpec;
  17. import java.security.spec.RSAPublicKeySpec;
  18. import java.security.spec.X509EncodedKeySpec;
  19. import javax.crypto.Cipher;
  20. /**
  21. * @author Mr.Zheng
  22. * @date 2014年8月22日 下午1:44:23
  23. */
  24. public final class RSAUtils
  25. {
  26. private static String RSA = "RSA";
  27. /**
  28. * 随机生成RSA密钥对(默认密钥长度为1024)
  29. *
  30. * @return
  31. */
  32. public static KeyPair generateRSAKeyPair()
  33. {
  34. return generateRSAKeyPair(1024);
  35. }
  36. /**
  37. * 随机生成RSA密钥对
  38. *
  39. * @param keyLength
  40. *            密钥长度,范围:512~2048<br>
  41. *            一般1024
  42. * @return
  43. */
  44. public static KeyPair generateRSAKeyPair(int keyLength)
  45. {
  46. try
  47. {
  48. KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
  49. kpg.initialize(keyLength);
  50. return kpg.genKeyPair();
  51. } catch (NoSuchAlgorithmException e)
  52. {
  53. e.printStackTrace();
  54. return null;
  55. }
  56. }
  57. /**
  58. * 用公钥加密 <br>
  59. * 每次加密的字节数,不能超过密钥的长度值减去11
  60. *
  61. * @param data
  62. *            需加密数据的byte数据
  63. * @param pubKey
  64. *            公钥
  65. * @return 加密后的byte型数据
  66. */
  67. public static byte[] encryptData(byte[] data, PublicKey publicKey)
  68. {
  69. try
  70. {
  71. Cipher cipher = Cipher.getInstance(RSA);
  72. // 编码前设定编码方式及密钥
  73. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  74. // 传入编码数据并返回编码结果
  75. return cipher.doFinal(data);
  76. } catch (Exception e)
  77. {
  78. e.printStackTrace();
  79. return null;
  80. }
  81. }
  82. /**
  83. * 用私钥解密
  84. *
  85. * @param encryptedData
  86. *            经过encryptedData()加密返回的byte数据
  87. * @param privateKey
  88. *            私钥
  89. * @return
  90. */
  91. public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey)
  92. {
  93. try
  94. {
  95. Cipher cipher = Cipher.getInstance(RSA);
  96. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  97. return cipher.doFinal(encryptedData);
  98. } catch (Exception e)
  99. {
  100. return null;
  101. }
  102. }
  103. /**
  104. * 通过公钥byte[](publicKey.getEncoded())将公钥还原,适用于RSA算法
  105. *
  106. * @param keyBytes
  107. * @return
  108. * @throws NoSuchAlgorithmException
  109. * @throws InvalidKeySpecException
  110. */
  111. public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException,
  112. InvalidKeySpecException
  113. {
  114. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
  115. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  116. PublicKey publicKey = keyFactory.generatePublic(keySpec);
  117. return publicKey;
  118. }
  119. /**
  120. * 通过私钥byte[]将公钥还原,适用于RSA算法
  121. *
  122. * @param keyBytes
  123. * @return
  124. * @throws NoSuchAlgorithmException
  125. * @throws InvalidKeySpecException
  126. */
  127. public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException,
  128. InvalidKeySpecException
  129. {
  130. PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
  131. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  132. PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
  133. return privateKey;
  134. }
  135. /**
  136. * 使用N、e值还原公钥
  137. *
  138. * @param modulus
  139. * @param publicExponent
  140. * @return
  141. * @throws NoSuchAlgorithmException
  142. * @throws InvalidKeySpecException
  143. */
  144. public static PublicKey getPublicKey(String modulus, String publicExponent)
  145. throws NoSuchAlgorithmException, InvalidKeySpecException
  146. {
  147. BigInteger bigIntModulus = new BigInteger(modulus);
  148. BigInteger bigIntPrivateExponent = new BigInteger(publicExponent);
  149. RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
  150. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  151. PublicKey publicKey = keyFactory.generatePublic(keySpec);
  152. return publicKey;
  153. }
  154. /**
  155. * 使用N、d值还原私钥
  156. *
  157. * @param modulus
  158. * @param privateExponent
  159. * @return
  160. * @throws NoSuchAlgorithmException
  161. * @throws InvalidKeySpecException
  162. */
  163. public static PrivateKey getPrivateKey(String modulus, String privateExponent)
  164. throws NoSuchAlgorithmException, InvalidKeySpecException
  165. {
  166. BigInteger bigIntModulus = new BigInteger(modulus);
  167. BigInteger bigIntPrivateExponent = new BigInteger(privateExponent);
  168. RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
  169. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  170. PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
  171. return privateKey;
  172. }
  173. /**
  174. * 从字符串中加载公钥
  175. *
  176. * @param publicKeyStr
  177. *            公钥数据字符串
  178. * @throws Exception
  179. *             加载公钥时产生的异常
  180. */
  181. public static PublicKey loadPublicKey(String publicKeyStr) throws Exception
  182. {
  183. try
  184. {
  185. byte[] buffer = Base64Utils.decode(publicKeyStr);
  186. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  187. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
  188. return (RSAPublicKey) keyFactory.generatePublic(keySpec);
  189. } catch (NoSuchAlgorithmException e)
  190. {
  191. throw new Exception("无此算法");
  192. } catch (InvalidKeySpecException e)
  193. {
  194. throw new Exception("公钥非法");
  195. } catch (NullPointerException e)
  196. {
  197. throw new Exception("公钥数据为空");
  198. }
  199. }
  200. /**
  201. * 从字符串中加载私钥<br>
  202. * 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。
  203. *
  204. * @param privateKeyStr
  205. * @return
  206. * @throws Exception
  207. */
  208. public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception
  209. {
  210. try
  211. {
  212. byte[] buffer = Base64Utils.decode(privateKeyStr);
  213. // X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
  214. PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
  215. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  216. return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
  217. } catch (NoSuchAlgorithmException e)
  218. {
  219. throw new Exception("无此算法");
  220. } catch (InvalidKeySpecException e)
  221. {
  222. throw new Exception("私钥非法");
  223. } catch (NullPointerException e)
  224. {
  225. throw new Exception("私钥数据为空");
  226. }
  227. }
  228. /**
  229. * 从文件中输入流中加载公钥
  230. *
  231. * @param in
  232. *            公钥输入流
  233. * @throws Exception
  234. *             加载公钥时产生的异常
  235. */
  236. public static PublicKey loadPublicKey(InputStream in) throws Exception
  237. {
  238. try
  239. {
  240. return loadPublicKey(readKey(in));
  241. } catch (IOException e)
  242. {
  243. throw new Exception("公钥数据流读取错误");
  244. } catch (NullPointerException e)
  245. {
  246. throw new Exception("公钥输入流为空");
  247. }
  248. }
  249. /**
  250. * 从文件中加载私钥
  251. *
  252. * @param keyFileName
  253. *            私钥文件名
  254. * @return 是否成功
  255. * @throws Exception
  256. */
  257. public static PrivateKey loadPrivateKey(InputStream in) throws Exception
  258. {
  259. try
  260. {
  261. return loadPrivateKey(readKey(in));
  262. } catch (IOException e)
  263. {
  264. throw new Exception("私钥数据读取错误");
  265. } catch (NullPointerException e)
  266. {
  267. throw new Exception("私钥输入流为空");
  268. }
  269. }
  270. /**
  271. * 读取密钥信息
  272. *
  273. * @param in
  274. * @return
  275. * @throws IOException
  276. */
  277. private static String readKey(InputStream in) throws IOException
  278. {
  279. BufferedReader br = new BufferedReader(new InputStreamReader(in));
  280. String readLine = null;
  281. StringBuilder sb = new StringBuilder();
  282. while ((readLine = br.readLine()) != null)
  283. {
  284. if (readLine.charAt(0) == '-')
  285. {
  286. continue;
  287. } else
  288. {
  289. sb.append(readLine);
  290. sb.append('\r');
  291. }
  292. }
  293. return sb.toString();
  294. }
  295. /**
  296. * 打印公钥信息
  297. *
  298. * @param publicKey
  299. */
  300. public static void printPublicKeyInfo(PublicKey publicKey)
  301. {
  302. RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
  303. System.out.println("----------RSAPublicKey----------");
  304. System.out.println("Modulus.length=" + rsaPublicKey.getModulus().bitLength());
  305. System.out.println("Modulus=" + rsaPublicKey.getModulus().toString());
  306. System.out.println("PublicExponent.length=" + rsaPublicKey.getPublicExponent().bitLength());
  307. System.out.println("PublicExponent=" + rsaPublicKey.getPublicExponent().toString());
  308. }
  309. public static void printPrivateKeyInfo(PrivateKey privateKey)
  310. {
  311. RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
  312. System.out.println("----------RSAPrivateKey ----------");
  313. System.out.println("Modulus.length=" + rsaPrivateKey.getModulus().bitLength());
  314. System.out.println("Modulus=" + rsaPrivateKey.getModulus().toString());
  315. System.out.println("PrivateExponent.length=" + rsaPrivateKey.getPrivateExponent().bitLength());
  316. System.out.println("PrivatecExponent=" + rsaPrivateKey.getPrivateExponent().toString());
  317. }
  318. }

上面需要注意的就是加密是有长度限制的,过长的话会抛异常!!!

代码中有些需要使用Base64再转换的,而java中不自带,Android中自带,所以自己写出一个来,方便Java后台使用

[java] view plaincopy
  1. package com.example.rsa;
  2. import java.io.UnsupportedEncodingException;
  3. /**
  4. * @author Mr.Zheng
  5. * @date 2014年8月22日 下午9:50:28
  6. */
  7. public class Base64Utils
  8. {
  9. private static char[] base64EncodeChars = new char[]
  10. { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
  11. 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  12. 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
  13. '6', '7', '8', '9', '+', '/' };
  14. private static byte[] base64DecodeChars = new byte[]
  15. { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  16. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
  17. 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
  18. 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
  19. 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1,
  20. -1, -1, -1 };
  21. /**
  22. * 加密
  23. *
  24. * @param data
  25. * @return
  26. */
  27. public static String encode(byte[] data)
  28. {
  29. StringBuffer sb = new StringBuffer();
  30. int len = data.length;
  31. int i = 0;
  32. int b1, b2, b3;
  33. while (i < len)
  34. {
  35. b1 = data[i++] & 0xff;
  36. if (i == len)
  37. {
  38. sb.append(base64EncodeChars[b1 >>> 2]);
  39. sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
  40. sb.append("==");
  41. break;
  42. }
  43. b2 = data[i++] & 0xff;
  44. if (i == len)
  45. {
  46. sb.append(base64EncodeChars[b1 >>> 2]);
  47. sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
  48. sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
  49. sb.append("=");
  50. break;
  51. }
  52. b3 = data[i++] & 0xff;
  53. sb.append(base64EncodeChars[b1 >>> 2]);
  54. sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
  55. sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
  56. sb.append(base64EncodeChars[b3 & 0x3f]);
  57. }
  58. return sb.toString();
  59. }
  60. /**
  61. * 解密
  62. *
  63. * @param str
  64. * @return
  65. */
  66. public static byte[] decode(String str)
  67. {
  68. try
  69. {
  70. return decodePrivate(str);
  71. } catch (UnsupportedEncodingException e)
  72. {
  73. e.printStackTrace();
  74. }
  75. return new byte[]
  76. {};
  77. }
  78. private static byte[] decodePrivate(String str) throws UnsupportedEncodingException
  79. {
  80. StringBuffer sb = new StringBuffer();
  81. byte[] data = null;
  82. data = str.getBytes("US-ASCII");
  83. int len = data.length;
  84. int i = 0;
  85. int b1, b2, b3, b4;
  86. while (i < len)
  87. {
  88. do
  89. {
  90. b1 = base64DecodeChars[data[i++]];
  91. } while (i < len && b1 == -1);
  92. if (b1 == -1)
  93. break;
  94. do
  95. {
  96. b2 = base64DecodeChars[data[i++]];
  97. } while (i < len && b2 == -1);
  98. if (b2 == -1)
  99. break;
  100. sb.append((char) ((b1 << 2) | ((b2 & 0x30) >>> 4)));
  101. do
  102. {
  103. b3 = data[i++];
  104. if (b3 == 61)
  105. return sb.toString().getBytes("iso8859-1");
  106. b3 = base64DecodeChars[b3];
  107. } while (i < len && b3 == -1);
  108. if (b3 == -1)
  109. break;
  110. sb.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));
  111. do
  112. {
  113. b4 = data[i++];
  114. if (b4 == 61)
  115. return sb.toString().getBytes("iso8859-1");
  116. b4 = base64DecodeChars[b4];
  117. } while (i < len && b4 == -1);
  118. if (b4 == -1)
  119. break;
  120. sb.append((char) (((b3 & 0x03) << 6) | b4));
  121. }
  122. return sb.toString().getBytes("iso8859-1");
  123. }
  124. }

最后就是真正使用它们了:

[java] view plaincopy
  1. package com.example.rsa;
  2. import java.io.InputStream;
  3. import java.security.PrivateKey;
  4. import java.security.PublicKey;
  5. import android.app.Activity;
  6. import android.os.Bundle;
  7. import android.util.Base64;
  8. import android.view.View;
  9. import android.view.View.OnClickListener;
  10. import android.widget.Button;
  11. import android.widget.EditText;
  12. public class MainActivity extends Activity implements OnClickListener
  13. {
  14. private Button btn1, btn2;// 加密,解密
  15. private EditText et1, et2, et3;// 需加密的内容,加密后的内容,解密后的内容
  16. /* 密钥内容 base64 code */
  17. private static String PUCLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfRTdcPIH10gT9f31rQuIInLwe"
  18. + "\r" + "7fl2dtEJ93gTmjE9c2H+kLVENWgECiJVQ5sonQNfwToMKdO0b3Olf4pgBKeLThra" + "\r"
  19. + "z/L3nYJYlbqjHC3jTjUnZc0luumpXGsox62+PuSGBlfb8zJO6hix4GV/vhyQVCpG" + "\r"
  20. + "9aYqgE7zyTRZYX9byQIDAQAB" + "\r";
  21. private static String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJ9FN1w8gfXSBP1/"
  22. + "\r" + "fWtC4gicvB7t+XZ20Qn3eBOaMT1zYf6QtUQ1aAQKIlVDmyidA1/BOgwp07Rvc6V/" + "\r"
  23. + "imAEp4tOGtrP8vedgliVuqMcLeNONSdlzSW66alcayjHrb4+5IYGV9vzMk7qGLHg" + "\r"
  24. + "ZX++HJBUKkb1piqATvPJNFlhf1vJAgMBAAECgYA736xhG0oL3EkN9yhx8zG/5RP/" + "\r"
  25. + "WJzoQOByq7pTPCr4m/Ch30qVerJAmoKvpPumN+h1zdEBk5PHiAJkm96sG/PTndEf" + "\r"
  26. + "kZrAJ2hwSBqptcABYk6ED70gRTQ1S53tyQXIOSjRBcugY/21qeswS3nMyq3xDEPK" + "\r"
  27. + "XpdyKPeaTyuK86AEkQJBAM1M7p1lfzEKjNw17SDMLnca/8pBcA0EEcyvtaQpRvaL" + "\r"
  28. + "n61eQQnnPdpvHamkRBcOvgCAkfwa1uboru0QdXii/gUCQQDGmkP+KJPX9JVCrbRt" + "\r"
  29. + "7wKyIemyNM+J6y1ZBZ2bVCf9jacCQaSkIWnIR1S9UM+1CFE30So2CA0CfCDmQy+y" + "\r"
  30. + "7A31AkB8cGFB7j+GTkrLP7SX6KtRboAU7E0q1oijdO24r3xf/Imw4Cy0AAIx4KAu" + "\r"
  31. + "L29GOp1YWJYkJXCVTfyZnRxXHxSxAkEAvO0zkSv4uI8rDmtAIPQllF8+eRBT/deD" + "\r"
  32. + "JBR7ga/k+wctwK/Bd4Fxp9xzeETP0l8/I+IOTagK+Dos8d8oGQUFoQJBAI4Nwpfo" + "\r"
  33. + "MFaLJXGY9ok45wXrcqkJgM+SN6i8hQeujXESVHYatAIL/1DgLi+u46EFD69fw0w+" + "\r" + "c7o0HLlMsYPAzJw="
  34. + "\r";
  35. @Override
  36. protected void onCreate(Bundle savedInstanceState)
  37. {
  38. super.onCreate(savedInstanceState);
  39. setContentView(R.layout.activity_main);
  40. initView();
  41. }
  42. private void initView()
  43. {
  44. btn1 = (Button) findViewById(R.id.btn1);
  45. btn2 = (Button) findViewById(R.id.btn2);
  46. btn1.setOnClickListener(this);
  47. btn2.setOnClickListener(this);
  48. et1 = (EditText) findViewById(R.id.et1);
  49. et2 = (EditText) findViewById(R.id.et2);
  50. et3 = (EditText) findViewById(R.id.et3);
  51. }
  52. @Override
  53. public void onClick(View v)
  54. {
  55. switch (v.getId())
  56. {
  57. // 加密
  58. case R.id.btn1:
  59. String source = et1.getText().toString().trim();
  60. try
  61. {
  62. // 从字符串中得到公钥
  63. // PublicKey publicKey = RSAUtils.loadPublicKey(PUCLIC_KEY);
  64. // 从文件中得到公钥
  65. InputStream inPublic = getResources().getAssets().open("rsa_public_key.pem");
  66. PublicKey publicKey = RSAUtils.loadPublicKey(inPublic);
  67. // 加密
  68. byte[] encryptByte = RSAUtils.encryptData(source.getBytes(), publicKey);
  69. // 为了方便观察吧加密后的数据用base64加密转一下,要不然看起来是乱码,所以解密是也是要用Base64先转换
  70. String afterencrypt = Base64Utils.encode(encryptByte);
  71. et2.setText(afterencrypt);
  72. } catch (Exception e)
  73. {
  74. e.printStackTrace();
  75. }
  76. break;
  77. // 解密
  78. case R.id.btn2:
  79. String encryptContent = et2.getText().toString().trim();
  80. try
  81. {
  82. // 从字符串中得到私钥
  83. // PrivateKey privateKey = RSAUtils.loadPrivateKey(PRIVATE_KEY);
  84. // 从文件中得到私钥
  85. InputStream inPrivate = getResources().getAssets().open("pkcs8_rsa_private_key.pem");
  86. PrivateKey privateKey = RSAUtils.loadPrivateKey(inPrivate);
  87. // 因为RSA加密后的内容经Base64再加密转换了一下,所以先Base64解密回来再给RSA解密
  88. byte[] decryptByte = RSAUtils.decryptData(Base64Utils.decode(encryptContent), privateKey);
  89. String decryptStr = new String(decryptByte);
  90. et3.setText(decryptStr);
  91. } catch (Exception e)
  92. {
  93. e.printStackTrace();
  94. }
  95. break;
  96. default:
  97. break;
  98. }
  99. }
  100. }

我把密钥放到assest资源文件夹里了,也可以直接使用字符串得到,上面注释掉了。

最后我们来看下效果吧:

源码下载地址:http://download.csdn.net/detail/bbld_/7806673

概述

RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困 难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。关于RSA其它需要了解的知识,参考维基百科:http://zh.wikipedia.org/zh-cn/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95

在项目开发中对于一些比较敏感的信息需要对其进行加密处理,我们就可以使用RSA这种非对称加密算法来对数据进行加密处理。

使用

秘钥对的生成

1、我们可以在代码里随机生成密钥对

[java] view plaincopy
  1. /**
  2. * 随机生成RSA密钥对
  3. *
  4. * @param keyLength
  5. *            密钥长度,范围:512~2048<br>
  6. *            一般1024
  7. * @return
  8. */
  9. public static KeyPair generateRSAKeyPair(int keyLength)
  10. {
  11. try
  12. {
  13. KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
  14. kpg.initialize(keyLength);
  15. return kpg.genKeyPair();
  16. } catch (NoSuchAlgorithmException e)
  17. {
  18. e.printStackTrace();
  19. return null;
  20. }
  21. }

通过这方法我们得到KeyPair后就可以调用 keyPair.getPrivate() 和 pair.getPublic()得到 私钥 和 公钥了。不过实际我们一般是使用2的方法把生成的密钥对保存起来,密钥自己保存,比如服务端,把公钥给客户端(Android)用于加密需要发送的数据,最后服务端根据私钥吧客户端加密的信息解密再处理。

2、通过OpenSSl工具生成密钥对

OpenSSl工具下载:OpenSSl工具 (64位的也可使用)使用OpenSSl工具生成密钥对的过程如下:

首先双击打开bin文件夹下的openssl.exe,打开之后是一个命令行窗口:

然后通过如下命令生成私钥:

[plain] view plaincopy
  1. genrsa -out rsa_private_key.pem 1024

这条命令是让openssl随机生成了一份私钥,加密长度是1024位, 密钥长度,范围:512~2048。执行完命令后就可在bin文件夹下看到rsa_private_key.pem文件了。

用文本类工具打开可看到里面的内容:

[plain] view plaincopy
  1. -----BEGIN RSA PRIVATE KEY-----
  2. MIICXQIBAAKBgQCfRTdcPIH10gT9f31rQuIInLwe7fl2dtEJ93gTmjE9c2H+kLVE
  3. NWgECiJVQ5sonQNfwToMKdO0b3Olf4pgBKeLThraz/L3nYJYlbqjHC3jTjUnZc0l
  4. uumpXGsox62+PuSGBlfb8zJO6hix4GV/vhyQVCpG9aYqgE7zyTRZYX9byQIDAQAB
  5. AoGAO9+sYRtKC9xJDfcocfMxv+UT/1ic6EDgcqu6Uzwq+Jvwod9KlXqyQJqCr6T7
  6. pjfodc3RAZOTx4gCZJverBvz053RH5GawCdocEgaqbXAAWJOhA+9IEU0NUud7ckF
  7. yDko0QXLoGP9tanrMEt5zMqt8QxDyl6Xcij3mk8rivOgBJECQQDNTO6dZX8xCozc
  8. Ne0gzC53Gv/KQXANBBHMr7WkKUb2i5+tXkEJ5z3abx2ppEQXDr4AgJH8Gtbm6K7t
  9. EHV4ov4FAkEAxppD/iiT1/SVQq20be8CsiHpsjTPiestWQWdm1Qn/Y2nAkGkpCFp
  10. yEdUvVDPtQhRN9EqNggNAnwg5kMvsuwN9QJAfHBhQe4/hk5Kyz+0l+irUW6AFOxN
  11. KtaIo3TtuK98X/yJsOAstAACMeCgLi9vRjqdWFiWJCVwlU38mZ0cVx8UsQJBALzt
  12. M5Er+LiPKw5rQCD0JZRfPnkQU/3XgyQUe4Gv5PsHLcCvwXeBcafcc3hEz9JfPyPi
  13. Dk2oCvg6LPHfKBkFBaECQQCODcKX6DBWiyVxmPaJOOcF63KpCYDPkjeovIUHro1x
  14. ElR2GrQCC/9Q4C4vruOhBQ+vX8NMPnO6NBy5TLGDwMyc
  15. -----END RSA PRIVATE KEY-----

这里面的内容是标准的ASCII字符,中间的一大串字符就是私钥数据了。

然后通过如下命令生成公钥:

[plain] view plaincopy
  1. rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout


打开文件看下里面的内容:

[plain] view plaincopy
  1. -----BEGIN PUBLIC KEY-----
  2. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfRTdcPIH10gT9f31rQuIInLwe
  3. 7fl2dtEJ93gTmjE9c2H+kLVENWgECiJVQ5sonQNfwToMKdO0b3Olf4pgBKeLThra
  4. z/L3nYJYlbqjHC3jTjUnZc0luumpXGsox62+PuSGBlfb8zJO6hix4GV/vhyQVCpG
  5. 9aYqgE7zyTRZYX9byQIDAQAB
  6. -----END PUBLIC KEY-----

可以看到是跟私钥的文件类似的。

这样密钥就基本生成了,不过这样密钥对的私钥是无法在代码中直接使用的,要想使用它需要借助RSAPrivateKeyStructure这个类,java是不自带的。所以为了方便使用,我们需要对私钥进行PKCS#8编码,命令如下:

[plain] view plaincopy
  1. pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt


这条命令的结果依然是在bin文件夹生成了pkcs8_rsa_private_key.pem文件,打开内容如下:

[plain] view plaincopy
  1. -----BEGIN PRIVATE KEY-----
  2. MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJ9FN1w8gfXSBP1/
  3. fWtC4gicvB7t+XZ20Qn3eBOaMT1zYf6QtUQ1aAQKIlVDmyidA1/BOgwp07Rvc6V/
  4. imAEp4tOGtrP8vedgliVuqMcLeNONSdlzSW66alcayjHrb4+5IYGV9vzMk7qGLHg
  5. ZX++HJBUKkb1piqATvPJNFlhf1vJAgMBAAECgYA736xhG0oL3EkN9yhx8zG/5RP/
  6. WJzoQOByq7pTPCr4m/Ch30qVerJAmoKvpPumN+h1zdEBk5PHiAJkm96sG/PTndEf
  7. kZrAJ2hwSBqptcABYk6ED70gRTQ1S53tyQXIOSjRBcugY/21qeswS3nMyq3xDEPK
  8. XpdyKPeaTyuK86AEkQJBAM1M7p1lfzEKjNw17SDMLnca/8pBcA0EEcyvtaQpRvaL
  9. n61eQQnnPdpvHamkRBcOvgCAkfwa1uboru0QdXii/gUCQQDGmkP+KJPX9JVCrbRt
  10. 7wKyIemyNM+J6y1ZBZ2bVCf9jacCQaSkIWnIR1S9UM+1CFE30So2CA0CfCDmQy+y
  11. 7A31AkB8cGFB7j+GTkrLP7SX6KtRboAU7E0q1oijdO24r3xf/Imw4Cy0AAIx4KAu
  12. L29GOp1YWJYkJXCVTfyZnRxXHxSxAkEAvO0zkSv4uI8rDmtAIPQllF8+eRBT/deD
  13. JBR7ga/k+wctwK/Bd4Fxp9xzeETP0l8/I+IOTagK+Dos8d8oGQUFoQJBAI4Nwpfo
  14. MFaLJXGY9ok45wXrcqkJgM+SN6i8hQeujXESVHYatAIL/1DgLi+u46EFD69fw0w+
  15. c7o0HLlMsYPAzJw=
  16. -----END PRIVATE KEY-----

可以看到中间的私钥内容有所变化了,这样的私钥我们在代码里就方便使用了。

以上的密钥文件使用时需要注意吧头和尾的字符串去掉,我们只取中间的内容。


代码中的使用

首先我们需要封装写个RSA的工具类,方便加密解密的操作。

[java] view plaincopy
  1. package com.example.rsa;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.InputStreamReader;
  6. import java.math.BigInteger;
  7. import java.security.KeyFactory;
  8. import java.security.KeyPair;
  9. import java.security.KeyPairGenerator;
  10. import java.security.NoSuchAlgorithmException;
  11. import java.security.PrivateKey;
  12. import java.security.PublicKey;
  13. import java.security.interfaces.RSAPrivateKey;
  14. import java.security.interfaces.RSAPublicKey;
  15. import java.security.spec.InvalidKeySpecException;
  16. import java.security.spec.PKCS8EncodedKeySpec;
  17. import java.security.spec.RSAPublicKeySpec;
  18. import java.security.spec.X509EncodedKeySpec;
  19. import javax.crypto.Cipher;
  20. /**
  21. * @author Mr.Zheng
  22. * @date 2014年8月22日 下午1:44:23
  23. */
  24. public final class RSAUtils
  25. {
  26. private static String RSA = "RSA";
  27. /**
  28. * 随机生成RSA密钥对(默认密钥长度为1024)
  29. *
  30. * @return
  31. */
  32. public static KeyPair generateRSAKeyPair()
  33. {
  34. return generateRSAKeyPair(1024);
  35. }
  36. /**
  37. * 随机生成RSA密钥对
  38. *
  39. * @param keyLength
  40. *            密钥长度,范围:512~2048<br>
  41. *            一般1024
  42. * @return
  43. */
  44. public static KeyPair generateRSAKeyPair(int keyLength)
  45. {
  46. try
  47. {
  48. KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
  49. kpg.initialize(keyLength);
  50. return kpg.genKeyPair();
  51. } catch (NoSuchAlgorithmException e)
  52. {
  53. e.printStackTrace();
  54. return null;
  55. }
  56. }
  57. /**
  58. * 用公钥加密 <br>
  59. * 每次加密的字节数,不能超过密钥的长度值减去11
  60. *
  61. * @param data
  62. *            需加密数据的byte数据
  63. * @param pubKey
  64. *            公钥
  65. * @return 加密后的byte型数据
  66. */
  67. public static byte[] encryptData(byte[] data, PublicKey publicKey)
  68. {
  69. try
  70. {
  71. Cipher cipher = Cipher.getInstance(RSA);
  72. // 编码前设定编码方式及密钥
  73. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  74. // 传入编码数据并返回编码结果
  75. return cipher.doFinal(data);
  76. } catch (Exception e)
  77. {
  78. e.printStackTrace();
  79. return null;
  80. }
  81. }
  82. /**
  83. * 用私钥解密
  84. *
  85. * @param encryptedData
  86. *            经过encryptedData()加密返回的byte数据
  87. * @param privateKey
  88. *            私钥
  89. * @return
  90. */
  91. public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey)
  92. {
  93. try
  94. {
  95. Cipher cipher = Cipher.getInstance(RSA);
  96. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  97. return cipher.doFinal(encryptedData);
  98. } catch (Exception e)
  99. {
  100. return null;
  101. }
  102. }
  103. /**
  104. * 通过公钥byte[](publicKey.getEncoded())将公钥还原,适用于RSA算法
  105. *
  106. * @param keyBytes
  107. * @return
  108. * @throws NoSuchAlgorithmException
  109. * @throws InvalidKeySpecException
  110. */
  111. public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException,
  112. InvalidKeySpecException
  113. {
  114. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
  115. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  116. PublicKey publicKey = keyFactory.generatePublic(keySpec);
  117. return publicKey;
  118. }
  119. /**
  120. * 通过私钥byte[]将公钥还原,适用于RSA算法
  121. *
  122. * @param keyBytes
  123. * @return
  124. * @throws NoSuchAlgorithmException
  125. * @throws InvalidKeySpecException
  126. */
  127. public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException,
  128. InvalidKeySpecException
  129. {
  130. PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
  131. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  132. PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
  133. return privateKey;
  134. }
  135. /**
  136. * 使用N、e值还原公钥
  137. *
  138. * @param modulus
  139. * @param publicExponent
  140. * @return
  141. * @throws NoSuchAlgorithmException
  142. * @throws InvalidKeySpecException
  143. */
  144. public static PublicKey getPublicKey(String modulus, String publicExponent)
  145. throws NoSuchAlgorithmException, InvalidKeySpecException
  146. {
  147. BigInteger bigIntModulus = new BigInteger(modulus);
  148. BigInteger bigIntPrivateExponent = new BigInteger(publicExponent);
  149. RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
  150. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  151. PublicKey publicKey = keyFactory.generatePublic(keySpec);
  152. return publicKey;
  153. }
  154. /**
  155. * 使用N、d值还原私钥
  156. *
  157. * @param modulus
  158. * @param privateExponent
  159. * @return
  160. * @throws NoSuchAlgorithmException
  161. * @throws InvalidKeySpecException
  162. */
  163. public static PrivateKey getPrivateKey(String modulus, String privateExponent)
  164. throws NoSuchAlgorithmException, InvalidKeySpecException
  165. {
  166. BigInteger bigIntModulus = new BigInteger(modulus);
  167. BigInteger bigIntPrivateExponent = new BigInteger(privateExponent);
  168. RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
  169. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  170. PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
  171. return privateKey;
  172. }
  173. /**
  174. * 从字符串中加载公钥
  175. *
  176. * @param publicKeyStr
  177. *            公钥数据字符串
  178. * @throws Exception
  179. *             加载公钥时产生的异常
  180. */
  181. public static PublicKey loadPublicKey(String publicKeyStr) throws Exception
  182. {
  183. try
  184. {
  185. byte[] buffer = Base64Utils.decode(publicKeyStr);
  186. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  187. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
  188. return (RSAPublicKey) keyFactory.generatePublic(keySpec);
  189. } catch (NoSuchAlgorithmException e)
  190. {
  191. throw new Exception("无此算法");
  192. } catch (InvalidKeySpecException e)
  193. {
  194. throw new Exception("公钥非法");
  195. } catch (NullPointerException e)
  196. {
  197. throw new Exception("公钥数据为空");
  198. }
  199. }
  200. /**
  201. * 从字符串中加载私钥<br>
  202. * 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。
  203. *
  204. * @param privateKeyStr
  205. * @return
  206. * @throws Exception
  207. */
  208. public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception
  209. {
  210. try
  211. {
  212. byte[] buffer = Base64Utils.decode(privateKeyStr);
  213. // X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
  214. PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
  215. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  216. return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
  217. } catch (NoSuchAlgorithmException e)
  218. {
  219. throw new Exception("无此算法");
  220. } catch (InvalidKeySpecException e)
  221. {
  222. throw new Exception("私钥非法");
  223. } catch (NullPointerException e)
  224. {
  225. throw new Exception("私钥数据为空");
  226. }
  227. }
  228. /**
  229. * 从文件中输入流中加载公钥
  230. *
  231. * @param in
  232. *            公钥输入流
  233. * @throws Exception
  234. *             加载公钥时产生的异常
  235. */
  236. public static PublicKey loadPublicKey(InputStream in) throws Exception
  237. {
  238. try
  239. {
  240. return loadPublicKey(readKey(in));
  241. } catch (IOException e)
  242. {
  243. throw new Exception("公钥数据流读取错误");
  244. } catch (NullPointerException e)
  245. {
  246. throw new Exception("公钥输入流为空");
  247. }
  248. }
  249. /**
  250. * 从文件中加载私钥
  251. *
  252. * @param keyFileName
  253. *            私钥文件名
  254. * @return 是否成功
  255. * @throws Exception
  256. */
  257. public static PrivateKey loadPrivateKey(InputStream in) throws Exception
  258. {
  259. try
  260. {
  261. return loadPrivateKey(readKey(in));
  262. } catch (IOException e)
  263. {
  264. throw new Exception("私钥数据读取错误");
  265. } catch (NullPointerException e)
  266. {
  267. throw new Exception("私钥输入流为空");
  268. }
  269. }
  270. /**
  271. * 读取密钥信息
  272. *
  273. * @param in
  274. * @return
  275. * @throws IOException
  276. */
  277. private static String readKey(InputStream in) throws IOException
  278. {
  279. BufferedReader br = new BufferedReader(new InputStreamReader(in));
  280. String readLine = null;
  281. StringBuilder sb = new StringBuilder();
  282. while ((readLine = br.readLine()) != null)
  283. {
  284. if (readLine.charAt(0) == '-')
  285. {
  286. continue;
  287. } else
  288. {
  289. sb.append(readLine);
  290. sb.append('\r');
  291. }
  292. }
  293. return sb.toString();
  294. }
  295. /**
  296. * 打印公钥信息
  297. *
  298. * @param publicKey
  299. */
  300. public static void printPublicKeyInfo(PublicKey publicKey)
  301. {
  302. RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
  303. System.out.println("----------RSAPublicKey----------");
  304. System.out.println("Modulus.length=" + rsaPublicKey.getModulus().bitLength());
  305. System.out.println("Modulus=" + rsaPublicKey.getModulus().toString());
  306. System.out.println("PublicExponent.length=" + rsaPublicKey.getPublicExponent().bitLength());
  307. System.out.println("PublicExponent=" + rsaPublicKey.getPublicExponent().toString());
  308. }
  309. public static void printPrivateKeyInfo(PrivateKey privateKey)
  310. {
  311. RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
  312. System.out.println("----------RSAPrivateKey ----------");
  313. System.out.println("Modulus.length=" + rsaPrivateKey.getModulus().bitLength());
  314. System.out.println("Modulus=" + rsaPrivateKey.getModulus().toString());
  315. System.out.println("PrivateExponent.length=" + rsaPrivateKey.getPrivateExponent().bitLength());
  316. System.out.println("PrivatecExponent=" + rsaPrivateKey.getPrivateExponent().toString());
  317. }
  318. }

上面需要注意的就是加密是有长度限制的,过长的话会抛异常!!!

代码中有些需要使用Base64再转换的,而java中不自带,Android中自带,所以自己写出一个来,方便Java后台使用

[java] view plaincopy
  1. package com.example.rsa;
  2. import java.io.UnsupportedEncodingException;
  3. /**
  4. * @author Mr.Zheng
  5. * @date 2014年8月22日 下午9:50:28
  6. */
  7. public class Base64Utils
  8. {
  9. private static char[] base64EncodeChars = new char[]
  10. { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
  11. 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  12. 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
  13. '6', '7', '8', '9', '+', '/' };
  14. private static byte[] base64DecodeChars = new byte[]
  15. { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  16. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
  17. 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
  18. 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
  19. 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1,
  20. -1, -1, -1 };
  21. /**
  22. * 加密
  23. *
  24. * @param data
  25. * @return
  26. */
  27. public static String encode(byte[] data)
  28. {
  29. StringBuffer sb = new StringBuffer();
  30. int len = data.length;
  31. int i = 0;
  32. int b1, b2, b3;
  33. while (i < len)
  34. {
  35. b1 = data[i++] & 0xff;
  36. if (i == len)
  37. {
  38. sb.append(base64EncodeChars[b1 >>> 2]);
  39. sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
  40. sb.append("==");
  41. break;
  42. }
  43. b2 = data[i++] & 0xff;
  44. if (i == len)
  45. {
  46. sb.append(base64EncodeChars[b1 >>> 2]);
  47. sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
  48. sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
  49. sb.append("=");
  50. break;
  51. }
  52. b3 = data[i++] & 0xff;
  53. sb.append(base64EncodeChars[b1 >>> 2]);
  54. sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
  55. sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
  56. sb.append(base64EncodeChars[b3 & 0x3f]);
  57. }
  58. return sb.toString();
  59. }
  60. /**
  61. * 解密
  62. *
  63. * @param str
  64. * @return
  65. */
  66. public static byte[] decode(String str)
  67. {
  68. try
  69. {
  70. return decodePrivate(str);
  71. } catch (UnsupportedEncodingException e)
  72. {
  73. e.printStackTrace();
  74. }
  75. return new byte[]
  76. {};
  77. }
  78. private static byte[] decodePrivate(String str) throws UnsupportedEncodingException
  79. {
  80. StringBuffer sb = new StringBuffer();
  81. byte[] data = null;
  82. data = str.getBytes("US-ASCII");
  83. int len = data.length;
  84. int i = 0;
  85. int b1, b2, b3, b4;
  86. while (i < len)
  87. {
  88. do
  89. {
  90. b1 = base64DecodeChars[data[i++]];
  91. } while (i < len && b1 == -1);
  92. if (b1 == -1)
  93. break;
  94. do
  95. {
  96. b2 = base64DecodeChars[data[i++]];
  97. } while (i < len && b2 == -1);
  98. if (b2 == -1)
  99. break;
  100. sb.append((char) ((b1 << 2) | ((b2 & 0x30) >>> 4)));
  101. do
  102. {
  103. b3 = data[i++];
  104. if (b3 == 61)
  105. return sb.toString().getBytes("iso8859-1");
  106. b3 = base64DecodeChars[b3];
  107. } while (i < len && b3 == -1);
  108. if (b3 == -1)
  109. break;
  110. sb.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));
  111. do
  112. {
  113. b4 = data[i++];
  114. if (b4 == 61)
  115. return sb.toString().getBytes("iso8859-1");
  116. b4 = base64DecodeChars[b4];
  117. } while (i < len && b4 == -1);
  118. if (b4 == -1)
  119. break;
  120. sb.append((char) (((b3 & 0x03) << 6) | b4));
  121. }
  122. return sb.toString().getBytes("iso8859-1");
  123. }
  124. }

最后就是真正使用它们了:

[java] view plaincopy
  1. package com.example.rsa;
  2. import java.io.InputStream;
  3. import java.security.PrivateKey;
  4. import java.security.PublicKey;
  5. import android.app.Activity;
  6. import android.os.Bundle;
  7. import android.util.Base64;
  8. import android.view.View;
  9. import android.view.View.OnClickListener;
  10. import android.widget.Button;
  11. import android.widget.EditText;
  12. public class MainActivity extends Activity implements OnClickListener
  13. {
  14. private Button btn1, btn2;// 加密,解密
  15. private EditText et1, et2, et3;// 需加密的内容,加密后的内容,解密后的内容
  16. /* 密钥内容 base64 code */
  17. private static String PUCLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfRTdcPIH10gT9f31rQuIInLwe"
  18. + "\r" + "7fl2dtEJ93gTmjE9c2H+kLVENWgECiJVQ5sonQNfwToMKdO0b3Olf4pgBKeLThra" + "\r"
  19. + "z/L3nYJYlbqjHC3jTjUnZc0luumpXGsox62+PuSGBlfb8zJO6hix4GV/vhyQVCpG" + "\r"
  20. + "9aYqgE7zyTRZYX9byQIDAQAB" + "\r";
  21. private static String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJ9FN1w8gfXSBP1/"
  22. + "\r" + "fWtC4gicvB7t+XZ20Qn3eBOaMT1zYf6QtUQ1aAQKIlVDmyidA1/BOgwp07Rvc6V/" + "\r"
  23. + "imAEp4tOGtrP8vedgliVuqMcLeNONSdlzSW66alcayjHrb4+5IYGV9vzMk7qGLHg" + "\r"
  24. + "ZX++HJBUKkb1piqATvPJNFlhf1vJAgMBAAECgYA736xhG0oL3EkN9yhx8zG/5RP/" + "\r"
  25. + "WJzoQOByq7pTPCr4m/Ch30qVerJAmoKvpPumN+h1zdEBk5PHiAJkm96sG/PTndEf" + "\r"
  26. + "kZrAJ2hwSBqptcABYk6ED70gRTQ1S53tyQXIOSjRBcugY/21qeswS3nMyq3xDEPK" + "\r"
  27. + "XpdyKPeaTyuK86AEkQJBAM1M7p1lfzEKjNw17SDMLnca/8pBcA0EEcyvtaQpRvaL" + "\r"
  28. + "n61eQQnnPdpvHamkRBcOvgCAkfwa1uboru0QdXii/gUCQQDGmkP+KJPX9JVCrbRt" + "\r"
  29. + "7wKyIemyNM+J6y1ZBZ2bVCf9jacCQaSkIWnIR1S9UM+1CFE30So2CA0CfCDmQy+y" + "\r"
  30. + "7A31AkB8cGFB7j+GTkrLP7SX6KtRboAU7E0q1oijdO24r3xf/Imw4Cy0AAIx4KAu" + "\r"
  31. + "L29GOp1YWJYkJXCVTfyZnRxXHxSxAkEAvO0zkSv4uI8rDmtAIPQllF8+eRBT/deD" + "\r"
  32. + "JBR7ga/k+wctwK/Bd4Fxp9xzeETP0l8/I+IOTagK+Dos8d8oGQUFoQJBAI4Nwpfo" + "\r"
  33. + "MFaLJXGY9ok45wXrcqkJgM+SN6i8hQeujXESVHYatAIL/1DgLi+u46EFD69fw0w+" + "\r" + "c7o0HLlMsYPAzJw="
  34. + "\r";
  35. @Override
  36. protected void onCreate(Bundle savedInstanceState)
  37. {
  38. super.onCreate(savedInstanceState);
  39. setContentView(R.layout.activity_main);
  40. initView();
  41. }
  42. private void initView()
  43. {
  44. btn1 = (Button) findViewById(R.id.btn1);
  45. btn2 = (Button) findViewById(R.id.btn2);
  46. btn1.setOnClickListener(this);
  47. btn2.setOnClickListener(this);
  48. et1 = (EditText) findViewById(R.id.et1);
  49. et2 = (EditText) findViewById(R.id.et2);
  50. et3 = (EditText) findViewById(R.id.et3);
  51. }
  52. @Override
  53. public void onClick(View v)
  54. {
  55. switch (v.getId())
  56. {
  57. // 加密
  58. case R.id.btn1:
  59. String source = et1.getText().toString().trim();
  60. try
  61. {
  62. // 从字符串中得到公钥
  63. // PublicKey publicKey = RSAUtils.loadPublicKey(PUCLIC_KEY);
  64. // 从文件中得到公钥
  65. InputStream inPublic = getResources().getAssets().open("rsa_public_key.pem");
  66. PublicKey publicKey = RSAUtils.loadPublicKey(inPublic);
  67. // 加密
  68. byte[] encryptByte = RSAUtils.encryptData(source.getBytes(), publicKey);
  69. // 为了方便观察吧加密后的数据用base64加密转一下,要不然看起来是乱码,所以解密是也是要用Base64先转换
  70. String afterencrypt = Base64Utils.encode(encryptByte);
  71. et2.setText(afterencrypt);
  72. } catch (Exception e)
  73. {
  74. e.printStackTrace();
  75. }
  76. break;
  77. // 解密
  78. case R.id.btn2:
  79. String encryptContent = et2.getText().toString().trim();
  80. try
  81. {
  82. // 从字符串中得到私钥
  83. // PrivateKey privateKey = RSAUtils.loadPrivateKey(PRIVATE_KEY);
  84. // 从文件中得到私钥
  85. InputStream inPrivate = getResources().getAssets().open("pkcs8_rsa_private_key.pem");
  86. PrivateKey privateKey = RSAUtils.loadPrivateKey(inPrivate);
  87. // 因为RSA加密后的内容经Base64再加密转换了一下,所以先Base64解密回来再给RSA解密
  88. byte[] decryptByte = RSAUtils.decryptData(Base64Utils.decode(encryptContent), privateKey);
  89. String decryptStr = new String(decryptByte);
  90. et3.setText(decryptStr);
  91. } catch (Exception e)
  92. {
  93. e.printStackTrace();
  94. }
  95. break;
  96. default:
  97. break;
  98. }
  99. }
  100. }

我把密钥放到assest资源文件夹里了,也可以直接使用字符串得到,上面注释掉了。

最后我们来看下效果吧:

源码下载地址:http://download.csdn.net/detail/bbld_/7806673

Android RSA加密解密,用于和服务器交互时的请求相关推荐

  1. Android RSA加密解密的 工具类的使用

    RSA 比较特殊,我们首先要生成私钥和公钥,然后在加密的时候,使用私钥加密,在解密的时候使用公钥解密. //RSA 的初始化,获得私钥和密钥public void rsaInit(){try {Key ...

  2. angular和JAVA实现aes、rsa加密解密,前后端交互,前端加解密和后端JAVA加解密实现

    今天实现了下AES和RSA加密解密,主要的功能是对前后端交互数据进行加密解密,为什么要用到两个算法呢,首先RSA默认的话加密长度是有限的100多个byte吧大约,并且需要公钥私钥,而AES加密没有限制 ...

  3. java android rsa加密解密_Android RSA数据加密与Java服务端RSA私钥解密出错问题

    1. 出错描述:服务RSA解密抛出javax.crypto.BadPaddingException: Decryption error 2.出错原因:Android系统使用的虚拟机(dalvik)跟S ...

  4. Android RSA加密解密

    转载: http://blog.csdn.net/bbld_/article/details/38777491 概述 RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素 ...

  5. java android rsa加密解密_Android RSA加密解密

    转载 http://blog.csdn.net/bbld_/article/details/38777491 RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十 ...

  6. ios android rsa加密解密,IOS RSA加密解密

    最近项目中对接口进行了rsa 加密. 写下过程以便复习 公钥私钥一般是有后台给的,这里为了方便 自己生成一对秘钥 生成公钥 私钥. 终端中: 生成原始 RSA私钥文件 private_key.pem ...

  7. C# Java间进行RSA加密解密交互

    C# Java间进行RSA加密解密交互 原文:C# Java间进行RSA加密解密交互 这里,讲一下RSA算法加解密在C#和Java之间交互的问题,这两天纠结了很久,也看了很多其他人写的文章,颇受裨益, ...

  8. android rsa加密工具类,GitHub - Lerist/encrypt: Android 加密解密工具包。

    Encrypt(加密工具) 字符串,byte[],文件等对象的加密和解密工具集合,包含了多种加密方案. 加密类型 摘要 相关方法 简单加密 换一种编码格式 Base64Util 单向加密 只能加密,不 ...

  9. C# Java间进行RSA加密解密交互(二)

    接着前面一篇文章C# Java间进行RSA加密解密交互,继续探讨这个问题. 在前面,虽然已经实现了C# Java间进行RSA加密解密交互,但是还是与项目中要求的有所出入.在项目中,客户端(Java)的 ...

最新文章

  1. spring boot整合spring security笔记
  2. 链接详解--多目标文件的链接
  3. 利用autobench测试web服务器极限并发数
  4. 解析.sens数据集
  5. python设置默认密码_使用Python生成随机密码的示例分享
  6. FusionCharts V3图表导出图片和PDF属性说明
  7. 纽交所决定将蛋壳公寓ADS摘牌
  8. Qt5.12 安装教程windows
  9. weblogic下载
  10. python随机密码生成代码大全_Python实现随机密码生成器
  11. 网络监控解决方案及拓扑图
  12. sublime使用技巧
  13. Ramp Number
  14. 前端js下载文件压缩包
  15. oracle spool循环导出文件,oracle 使用spool导出数据到文件
  16. 微型计算机逻辑元件有哪些,目前普遍使用的微型计算机所采用的逻辑元件有哪些...
  17. 统计思维——通过统计方法解决分析问题
  18. 假期,推荐豆瓣评分 9.0 以上的 100 部必看电影
  19. 计算机图学概念,计算机图形学(概念).doc
  20. VLC android 3.0解码器使用及移植TV项目调研

热门文章

  1. php环境下安装并运行laravel教程
  2. autocad.net将Geometry对象转换为Database对象
  3. nanopi neo core学习(二)网络设置
  4. foxmail邮件只能显示邮件头,不能显示内容
  5. 剪视频一点都不难,多款超实用剪辑软件全方位评测!
  6. SQL干货丨关于分组和聚合函数,如何实现查询排名?!
  7. python图像处理:全景图片转鱼眼图片(鱼眼矫正)
  8. AB实验结果分析01-保证实验分析结果的准确性
  9. uniapp中手持机激光扫描
  10. mysql函数 游标_存储过程/游标/mysql 函数