遇到的问题

在一个与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的更多相关文章

  1. 【go语言】RSA加解密

    关于go语言的RSA加解密的介绍,这里有一篇文章,已经介绍的很完整了. 对应的go语言的加解密代码,参考git. 因为原文跨语言是跟php,我这里要跟c语言进行交互,所以,这里贴上c语言的例子. 参考 ...

  2. Java中的RSA加解密工具类:RSAUtils

    本人手写已测试,大家可以参考使用 package com.mirana.frame.utils.encrypt; import com.mirana.frame.utils.log.LogUtils; ...

  3. 前后端java+vue 实现rsa 加解密与摘要签名算法

    RSA 加密.解密.签名.验签.摘要,前后端java+vue联调测试通过 直接上代码 // 注意:加密密文与签名都是唯一的,不会变化.// 注意:vue 端密钥都要带pem格式.java 不要带pem ...

  4. Java和C# RSA加解密相互通信和使用公钥加密传输

    关于JAVA和C#加解密通讯的话,可以用这个BouncyCastle插件,会帮助你解决很多问题 http://www.bouncycastle.org/ //c#使用java给的公钥进行rsa加密 p ...

  5. java集成支付宝移动快捷支付时报错java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence

    出错原因是代码中的私钥设置错误,不是填原始的私钥,而是转换为PKCS8格式的私钥(Java格式的) ,改成后就会报创建交易异常了

  6. RSA加密 抛异常 algid parse error, not a sequence

    JDK1.8环境 参考:BouncyCastle的使用:https://blog.csdn.net/qq_29583513/article/details/78866461 可解决 公钥解密 私钥加密 ...

  7. openssl在多平台和多语言之间进行RSA加解密注意事项

    首先说一下平台和语言: 系统平台为CentOS6.3,RSA加解密时使用NOPADDING进行填充 1)使用C/C++调用系统自带的openssl 2)Android4.2模拟器,第三方openssl ...

  8. 全面解决.Net与Java互通时的RSA加解密问题,使用PEM格式的密钥文件

    作者: zyl910 一.缘由 RSA是一种常用的非对称加密算法.所以有时需要在不用编程语言中分别使用RSA的加密.解密.例如用Java做后台服务端,用C#开发桌面的客户端软件时. 由于 .Net.J ...

  9. Rsa加解密Java、C#、php通用代码 密钥转换工具

    之前发了一篇"TripleDes的加解密Java.C#.php通用代码",后面又有项目用到了Rsa加解密,还是在不同系统之间进行交互,Rsa在不同语言的密钥格式不一样,所以过程中主 ...

随机推荐

  1. .NET Core实战项目之CMS 第七章 设计篇-用户权限极简设计全过程

    写在前面 这篇我们对用户权限进行极简设计并保留其扩展性.首先很感谢大家的阅读,前面六章我带着大家快速入门了ASP.NET Core.ASP.NET Core的启动过程源码解析及配置文件的加载过程源码解 ...

  2. SpringCloud Config客户端

     SpringCloud Config服务端 1.导入依赖 <dependency> <groupId>org.springframework.cloud</groupI ...

  3. 【Java基础】【19异常&IO(File类)】

    19.01_异常(异常的概述和分类) A:异常的概述 异常就是Java程序在运行过程中出现的错误. B:异常的分类 通过API查看Throwable Error 服务器宕机,数据库崩溃等 Except ...

  4. C#线程安全使用(三)

    在讲CancellationTokenSource之前我决定先讲一下lock和Interlocked,如果能很好的理解这两个,再去理解CancellationTokenSource就会方便很多,由于我 ...

  5. mysql数据库备份并且实现远程复制

    一.实现ssh 远程登陆 机器环境: 192.167.33.108 clent 用户:crawler 192.167.33.77 server 用户:crawler 1.客户端 生成密钥 /home/ ...

  6. Spring Boot(十四)RabbitMQ延迟队列

    一.前言 延迟队列的使用场景:1.未按时支付的订单,30分钟过期之后取消订单:2.给活跃度比较低的用户间隔N天之后推送消息,提高活跃度:3.过1分钟给新注册会员的用户,发送注册邮件等. 实现延迟队列的 ...

  7. [转]nodeJs--koa2 REST API

    本文转自:https://blog.csdn.net/davidPan1234/article/details/83413958 REST API规范编写REST API,实际上就是编写处理HTTP请 ...

  8. MySQL 笔记整理(3) --事务隔离,为什么你改了我还看不见?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 3) --事务隔离,为什么你改了我还看不见? 简单来说,事务就是要保证一组数据操作,要么全部成功,要么全部失败.在MySQL中,事务 ...

  9. C# ListBox实现显示插入最新的数据的方法

    在我们使用ListBox控件时,如果我们在里面不断的添加一条条数据,但是在我们添加的数据过多超过了ListBox显示的窗口时(此时会产生滑动条), 发现我们无法看到最新添加的数据.实现倒序显示此处有两 ...

  10. WordPress在Centos下Apache设置伪静态方法

    1.设置httpd.conf文件 1.1 添加或取消注释这段代码 LoadModule rewrite_module modules/mod_rewrite.so 1.2 运行httpd -M查看这个 ...