与非java语言使用RSA加解密遇到的问题:algid parse error, not a sequence
遇到的问题
在一个与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 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
与非java语言使用RSA加解密遇到的问题:algid parse error, not a sequence的更多相关文章
- 【go语言】RSA加解密
关于go语言的RSA加解密的介绍,这里有一篇文章,已经介绍的很完整了. 对应的go语言的加解密代码,参考git. 因为原文跨语言是跟php,我这里要跟c语言进行交互,所以,这里贴上c语言的例子. 参考 ...
- Java中的RSA加解密工具类:RSAUtils
本人手写已测试,大家可以参考使用 package com.mirana.frame.utils.encrypt; import com.mirana.frame.utils.log.LogUtils; ...
- 前后端java+vue 实现rsa 加解密与摘要签名算法
RSA 加密.解密.签名.验签.摘要,前后端java+vue联调测试通过 直接上代码 // 注意:加密密文与签名都是唯一的,不会变化.// 注意:vue 端密钥都要带pem格式.java 不要带pem ...
- Java和C# RSA加解密相互通信和使用公钥加密传输
关于JAVA和C#加解密通讯的话,可以用这个BouncyCastle插件,会帮助你解决很多问题 http://www.bouncycastle.org/ //c#使用java给的公钥进行rsa加密 p ...
- java集成支付宝移动快捷支付时报错java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
出错原因是代码中的私钥设置错误,不是填原始的私钥,而是转换为PKCS8格式的私钥(Java格式的) ,改成后就会报创建交易异常了
- RSA加密 抛异常 algid parse error, not a sequence
JDK1.8环境 参考:BouncyCastle的使用:https://blog.csdn.net/qq_29583513/article/details/78866461 可解决 公钥解密 私钥加密 ...
- openssl在多平台和多语言之间进行RSA加解密注意事项
首先说一下平台和语言: 系统平台为CentOS6.3,RSA加解密时使用NOPADDING进行填充 1)使用C/C++调用系统自带的openssl 2)Android4.2模拟器,第三方openssl ...
- 全面解决.Net与Java互通时的RSA加解密问题,使用PEM格式的密钥文件
作者: zyl910 一.缘由 RSA是一种常用的非对称加密算法.所以有时需要在不用编程语言中分别使用RSA的加密.解密.例如用Java做后台服务端,用C#开发桌面的客户端软件时. 由于 .Net.J ...
- Rsa加解密Java、C#、php通用代码 密钥转换工具
之前发了一篇"TripleDes的加解密Java.C#.php通用代码",后面又有项目用到了Rsa加解密,还是在不同系统之间进行交互,Rsa在不同语言的密钥格式不一样,所以过程中主 ...
随机推荐
- python中的pip
python中的pip python有两个著名的包管理工具,其中,pip是一个.它对python的包进行管理和升级等操作. 问题一:pip本地的模块安装在哪里? 使用pip install numpy ...
- Spring Cloud中的负载均衡策略
在上篇博客(Spring Cloud中负载均衡器概览)中,我们大致的了解了一下Spring Cloud中有哪些负载均衡器,但是对于负载均衡策略我们并没有去详细了解,我们只是知道在BaseLoadBal ...
- 带着新人学springboot的应用04(springboot+mybatis+redis 完)
对于缓存也说了比较多了,大家对下图这一堆配置类现在应该有些很粗略的认识了(因为我也就很粗略的认识了一下,哈哈!),咳,那么我们怎么切换这个缓存呢?(就是不用springboot提供的默认的Simple ...
- Mongodb副本集--Out of memory: Kill process 37325 (mongod)
1.Mongodb副本集--Out of memory: Kill process 37325 (mongod) 场景描述: 恢复一个22TB数据的mongodb实例的时候. 将备用结点加入mongo ...
- 第19章 定义资源 - Identity Server 4 中文文档(v1.0.0)
您通常在系统中定义的第一件事是您要保护的资源.这可能是您的用户的身份信息,如个人资料数据或电子邮件地址,或访问API. 注意 您可以使用C#对象模型定义资源 - 或从数据存储加载它们.IResourc ...
- js如何使用正则表达式实现过滤HTML标签?(/<[^<>]+>/g)
js如何使用正则表达式实现过滤HTML标签?(/<[^<>]+>/g) 一.总结 js进阶正则表达式实现过滤HTML标签(<>标签中不能包含标签实现过滤HTML标签 ...
- .Net Core 编码规范
.Net Core 编码规范 标签: 未分类 概述 规范制定原则 方便代码的交流和维护. 不影响编码的效率,不与大众习惯冲突. 使代码更美观.阅读更方便. 使代码的逻辑更清晰.更易于理解. 术语定义 ...
- C#把动态创建的多个控件中指定控件显示在最上层
在创建多个控件后,有些控件会发生重叠,那么就需要在鼠标按下它时能显示在最上层,下面通过先将按下操作的控件删除然后再重建,就达到让它显示在最上层了 //控件按下 private void picture ...
- Java开发笔记(五十四)内部类和嵌套类
通常情况下,一个Java代码文件只定义一个类,即使两个类是父类与子类的关系,也要把它们拆成两个代码文件分别定义.可是有些事物相互之间密切联系,又不同于父子类的继承关系,比如一棵树会开很多花朵,这些花儿 ...
- 常用的Arrays类和二维数组以及二分法的介绍
---恢复内容开始--- 1.Array类 Array中包含了许多数组的常用操作,较为常见的有: (1)快速输出 import java.util.Arrays; public class Test{ ...