在一個與Ruby語言對接的項目中,決定使用RSA算法來做爲數據傳輸的加密與簽名算法。可是,在使用Ruby生成後給個人私鑰時,卻發生了異常:IOException: algid parse error, not a sequencejava
一般JAVA中使用的RSA私鑰格式必須爲PKCS8格式,可是Ruby可使用pkcs1格式的私鑰。另外,在使用openssl生成RSA密鑰對時,若是須要獲得PKCS8格式的私鑰須要多進行一步操做,所以可能爲了麻煩,Ruby方就直接使用PKCS1格式的私鑰。web
這對於JAVA來講很是難受,但只要知道了緣由,解決起來並非難事。可能會有將PKCS1轉爲PKCS8格式的JAVA代碼,可是網上資料很少,因而只好使用簡單粗暴的辦法,手動使用工具來轉換私鑰的格式。算法
下載openssl工具,將私鑰按照指定格式輸入至一個pem文件中,而後複製到openssl目錄下,打開openssl後輸入命令:shell
pkcs8 -topk8 -inform PEM -in 文件名.pem -outform PEM -nocrypt
顯示的私鑰即爲PKCS8格式的私鑰。安全
PS:java使用時務必刪除首尾行的「-----」字符串以及全部換行。工具
點我跳轉網站
用法與上面的方法一致,記得刪除無關字符。ui
對於RSA的密鑰對,一般公鑰都是PKCS8格式的,可是說不定就會遇到PKCS1格式,上面的網站就不能用了,別急,還有一個呢:加密
支持公鑰的轉換.net
RSA是一種非對稱加密算法,簡單的來講就是加密與解密的所使用的密鑰都不相同,所以相對於加解密都使用一個密鑰的對稱加密算法來講具備更好的安全性。此外,它還能夠做爲數字簽名算法,使用數字簽名的目的就是爲了驗證數據的來源是否正確以及數據是否被修改過。
這裏簡單放一下本人使用的RSA工具類,包含了生成密鑰對、加解密以及簽名和校驗簽名的方法。
/** * RSA加解密、建立與校驗簽名的工具類 */ public class RSAUtils { public static final String PUBLIC_KEY = "PUBLIC_KEY"; public static final String PRIVATE_KEY = "PRIVATE_KEY"; private static final Base64.Encoder base64Encoder = Base64.getEncoder(); private static final Base64.Decoder base64Decoder = Base64.getDecoder(); private static final String ALGORITHM = "RSA"; /** * 簽名算法 */ private static final String SIGN_TYPE = "SHA1withRSA"; /** * 密鑰長度 */ private static final Integer KEY_LENGTH = 1024; /** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 117; /** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** * 生成祕鑰對,公鑰和私鑰 * * @return 祕鑰鍵值對 * @throws Exception 建立祕鑰對異常 */ public static Map<String, Key> genKeyPair() throws Exception { Map<String, Key> keyMap = new HashMap<>(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM); keyPairGenerator.initialize(KEY_LENGTH); // 祕鑰字節數 KeyPair keyPair = keyPairGenerator.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 公鑰加密 * * @param data 加密前數據 * @param publicKey 公鑰 * @return 加密後數據 * @throws Exception 加密異常 */ public static byte[] encryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception { // 加密數據,分段加密 Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int inputLength = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offset = 0; byte[] cache; int i = 0; while (inputLength - offset > 0) { if (inputLength - offset > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offset, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offset, inputLength - offset); } out.write(cache, 0, cache.length); i++; offset = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } public static byte[] encryptByPublicKey(byte[] data, String publicKeyBase64Encoded) throws Exception { return encryptByPublicKey(data, parseString2PublicKey(publicKeyBase64Encoded)); } /** * 私鑰解密 * * @param data 解密前數據 * @param privateKey 私鑰 * @return 解密後數據 * @throws Exception 解密異常 */ public static byte[] decryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception { // 解密數據,分段解密 Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, privateKey); int inputLength = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offset = 0; byte[] cache; int i = 0; while (inputLength - offset > 0) { if (inputLength - offset > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(data, offset, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(data, offset, inputLength - offset); } out.write(cache); i++; offset = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } public static byte[] decryptByPrivateKey(byte[] data, String privateKeyBase64Encoded) throws Exception { return decryptByPrivateKey(data, parseString2PrivateKey(privateKeyBase64Encoded)); } /** * 建立簽名 * * @param source 要簽名的信息 * @param privateKey 私鑰 * @return 簽名 * @throws Exception 簽名異常 */ public static byte[] createSign(String source, PrivateKey privateKey) throws Exception { Signature signet = Signature.getInstance(SIGN_TYPE); signet.initSign(privateKey); signet.update(source.getBytes()); return signet.sign(); } public static byte[] createSign(String source, String privateKeyBase64Encoded) throws Exception { return createSign(source, parseString2PrivateKey(privateKeyBase64Encoded)); } /** * 校驗簽名 * * @param expected 指望信息 * @param sign 簽名 * @param publicKey 公鑰 * @return 結果 * @throws Exception 校驗異常 */ public static boolean checkSign(String expected, byte[] sign, PublicKey publicKey) throws Exception { Signature signetCheck = Signature.getInstance(SIGN_TYPE); signetCheck.initVerify(publicKey); signetCheck.update(expected.getBytes()); return signetCheck.verify(sign); } public static boolean checkSign(String expected, byte[] sign, String publicKeyBase64Encoded) throws Exception { return checkSign(expected, sign, parseString2PublicKey(publicKeyBase64Encoded)); } /** * 將base64格式的公鑰轉換爲對象 * * @param publicKeyBase64Encoded base64的公鑰 * @return 公鑰 * @throws Exception 轉換異常 */ public static PublicKey parseString2PublicKey(String publicKeyBase64Encoded) throws Exception { return KeyFactory.getInstance(ALGORITHM).generatePublic( new X509EncodedKeySpec(base64Decoder.decode(publicKeyBase64Encoded))); } /** * 將base64格式的私鑰轉換爲對象 * * @param privateKeyBase64Encoded base64的私鑰 * @return 私鑰 * @throws Exception 轉換異常 */ public static PrivateKey parseString2PrivateKey(String privateKeyBase64Encoded) throws Exception { return KeyFactory.getInstance(ALGORITHM).generatePrivate( new PKCS8EncodedKeySpec(base64Decoder.decode(privateKeyBase64Encoded))); } }
另附調用方法:
public static void main(String[] args) throws Exception { // write your code here // 建立密鑰對 Map<String, Key> map = RSAUtils.genKeyPair(); PublicKey publicKey = (PublicKey) map.get(RSAUtils.PUBLIC_KEY); PrivateKey privateKey = (PrivateKey) map.get(RSAUtils.PRIVATE_KEY); System.out.println("建立的密鑰對:"); System.out.println("公鑰:" + Base64.getEncoder().encodeToString(publicKey.getEncoded())); System.out.println("私鑰:" + Base64.getEncoder().encodeToString(privateKey.getEncoded())); String info = "hello world!"; System.out.println("原文爲:" + info); String str = Base64.getEncoder().encodeToString(RSAUtils.encryptByPublicKey(info.getBytes(), publicKey)); String sign = Base64.getEncoder().encodeToString(RSAUtils.createSign(info, privateKey)); System.out.println(">>>>>>>>>>>"); System.out.println("密文爲:" + str); System.out.println("簽名爲:" + sign); System.out.println(">>>>>>>>>>>"); String resultInfo = new String(RSAUtils.decryptByPrivateKey(Base64.getDecoder().decode(str), privateKey)); Boolean resultSign = RSAUtils.checkSign(info, Base64.getDecoder().decode(sign), publicKey); System.out.println(String.format("解密結果:%s,簽名校驗結果:%s", resultInfo, resultSign)); }
執行結果:
建立的密鑰對: 公鑰:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCTWSrzD61bczhKTrar8Xu4sm6zSVu+xo2ur0b4iSif0Xufm7OK/T2k0jwjdvTSg7BoR0dqXMjvlPEyUIZORFcT2HzNnl58zwzdW7S2XeMFtL10SBZpjcp1zPGbiJPde6fzqFNDLKJgj4P37f+BTJTr7UORSMdFr2OwrLOvWnUgEQIDAQAB 私鑰:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJNZKvMPrVtzOEpOtqvxe7iybrNJW77Gja6vRviJKJ/Re5+bs4r9PaTSPCN29NKDsGhHR2pcyO+U8TJQhk5EVxPYfM2eXnzPDN1btLZd4wW0vXRIFmmNynXM8ZuIk917p/OoU0MsomCPg/ft/4FMlOvtQ5FIx0WvY7Css69adSARAgMBAAECgYBJbGhjgA9hf5OwK3MJUSbWjUtuWYK3GNenET5rQGWW5dsVWI/qFXDvPber8G3krKxt+f7TOHMEN5LNAKU8QP+maGXL99uzoBHf6kHQWgwYWvD3kHfJEww1nv3/9a6P9Z0ZiL4DfiYy1tWCZ9gv6KLID05mC4NXiEr4TYhkcyYT8QJBANdP5iDDAJxKfLuFu00Y5jJ+Tuee7nRUjrSob2jRRBrZcsccCITR34aOr1+pZwZCPoHisyWjQL+mi/JKV9Y3Iw0CQQCvMWaok2g55ZEsBMlB38t/UfavGvnLqd7StZg+J+n0VFCfr05QiW9tn+IEtZGOHY317BgrpJ5dhVclIH5g3EAVAkBsWqwwLpJfFOlCoaFJwk8OeBwTWhscdfU/G0i90hpY/LdTVls/JDM+Dw5YsPLE5o94Y/LN7SNHj3P8IcekaSj9AkBvtnKdwBFQCeD+Trb++HPM5jkFA5CRm+poNj+0MsNud21JxgGMPXb+UltPYXBFTPc+/6OSANCzFdmx5PxxS0DZAkEAuicRUzcZThNu/9XWA24fU1w1+fT2T4hgANYmTJntaw+0m5cB03UQvvadGb9gslbDgaLWALS9mOLTPy+3nOHdng== 原文爲:hello world! >>>>>>>>>>> 密文爲:kiRSpYMdfcgoWosXVwKHUZQUzFAykiSqF3HQba0QHBwW7UEUDE9pZcx2lZxlHZzS3hhLeRl9CxwpF6ZE5Mza1GuW8DnZJ0k3RFlyyYaTvUWCH0GRq1BgRB8f/ouwbw2opdVd2kIfltRWWU4gvvyvFs2wPfZnyDsMJpyM3GpwnzE= 簽名爲:BQ5R0nGWj9IXNhqiZjCR23YTPKhS3ryAZEO76EaaF6awmEeL1/Ptf0IL8bFD57JurM2aybF6MXkFPb4dMaotZoyiHUMcUdnhLXOpRVvbtDXuyDjCWTwUqqjMyvO9qNm9s16veJj7B4Cu5r1jw4M6wR7vfDJm+89Amzrh+6dG+l0= >>>>>>>>>>> 解密結果:hello world!,簽名校驗結果:true