遇到的问题
在一个与Ruby语言对接的项目中,决定使用RSA算法来作为数据传输的加密与签名算法。但是,在使用Ruby生成后给我的私钥时,却发生了异常:IOException: algid parse error, not a sequence
原因以及解决办法
通常JAVA中使用的RSA私钥格式必须为PKCS8格式,但是Ruby可以使用pkcs1格式的私钥。另外,在使用openssl生成RSA密钥对时,如果需要得到PKCS8格式的私钥需要多进行一步操作,因此可能为了麻烦,Ruby方就直接使用PKCS1格式的私钥。
这对于JAVA来说非常难受,但只要知道了原因,解决起来并不是难事。可能会有将PKCS1转为PKCS8格式的JAVA代码,但是网上资料不多,于是只好使用简单粗暴的办法,手动使用工具来转换私钥的格式。
使用openssl转换
下载openssl工具,将私钥按照指定格式输入至一个pem文件中,然后复制到openssl目录下,打开openssl后输入命令:
pkcs8 -topk8 -inform PEM -in 文件名.pem -outform PEM -nocrypt
显示的私钥即为PKCS8格式的私钥。
PS:java使用时务必删除首尾行的“-----”字符串以及所有换行。
使用在线工具
用法与上面的方法一致,记得删除无关字符。
对于RSA的密钥对,通常公钥都是PKCS8格式的,但是说不定就会遇到PKCS1格式,上面的网站就不能用了,别急,还有一个呢:
RSA算法
RSA是一种非对称加密算法,简单的来说就是加密与解密的所使用的密钥都不相同,因此相对于加解密都使用一个密钥的对称加密算法来说具有更好的安全性。此外,它还可以作为数字签名算法,使用数字签名的目的就是为了验证数据的来源是否正确以及数据是否被修改过。
Java工具类
这里简单放一下本人使用的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 MapgenKeyPair() throws Exception { Map 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 // 创建密钥对 Mapmap = 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