与非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在不同语言的密钥格式不一样,所以过程中主 ...
随机推荐
- zabbix备份数据库
#全库备份(数据量大很慢且会告警) mysqldump -uzabbix -pzabbix --opt zabbix | bzip2 > zabbix.sql.bz2 #备份配置表 mysqld ...
- Error: Cannot fit requested classes in a single dex file (# methods: 149346 > 65536)
引用第三方库的jar文件,都有可能会触发这个错误.解决方案如下: 参考<[Android]Cannot fit requested classes in a single dex file. T ...
- pyhive 连接 Hive 时错误
一.User: xx is not allowed to impersonate xxx' 解决办法:修改 core-site.xml 文件,加入下面的内容后重启 hadoop. <proper ...
- SpringBoot修改Redis序列化方式
前言 由于Springboot默认提供了序列化方式并不是非常理想,对于高要求的情况下,序列化的速度和序列化之后大小有要求的情况下,不能满足,所以可能需要更换序列化的方式. 这里主要记录更换序列化的方式 ...
- git版本控制工具的使用
目录 git版本管理工具使用 一丶Git的下载与安装 1.windows下的git的下载与安装 2.linux下的git安装 二丶常用命令 三丶Git仓库 1.配置仓库信息 2.仓库的创建于管理 四丶 ...
- 如何定制Linux外围文件系统?
本文由云+社区发表 作者:我是乖宝宝哦 一般来说,我们所说的Linux系统指的是各种基于Linux Kernel和GNU Project的操作系统发行版.为了掌握Linux操作系统的使用,了解 Lin ...
- 小程序开发笔记【一】,查询用户参与活动列表 left join on的用法
今天在做一个用户活动查询功能的时候,查询参与的活动.正常,使用egg-mysql查询数据一般会这么写 result = await this.app.mysql.select('tb_activity ...
- 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Bootable image格式与加载过程. 在i.MXRT启动系列第三篇文章 Serial Down ...
- CAN总线学习记录之一:CAN简介
CAN 是 Controller Area Net 的缩写,即控制器局部网,是一种有效支持分布控制或实时控制的串行通信网络.CAN 是德国 Bosch 公司为汽车的监测.控制系统而设计的,如控制发动机 ...
- Springboot 系列(一)Spring Boot 入门篇
注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别. 前言 由于 J2EE 的开发变得笨重,繁多的配置, ...