遇到的问题
在一个与 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 使用时务必删除首尾行的 "-----" 字符串以及所有换行.
使用在线工具
点我跳转 http://tool.chacuo.net/cryptrsapkcs1pkcs8
用法与上面的方法一致, 记得删除无关字符.
对于 RSA 的密钥对, 通常公钥都是 PKCS8 格式的, 但是说不定就会遇到 PKCS1 格式, 上面的网站就不能用了, 别急, 还有一个呢:
支持公钥的转换 http://www.ssleye.com/web/pkcs
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 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
来源: http://www.bubuko.com/infodetail-2688287.html