遇到的问题

在一个与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. 『Kruscal重构树 Exkruscal』

    新增一道例题及讲解 Exkruscal \(Exkruscal\)又称\(Kruscal\)重构树,是一种利用经典算法\(Kruscal\)来实现的构造算法,可以将一张无向图重构为一棵具有\(2n-1 ...

  2. MVC从Controller到View的呈现

    图说MVC底层运行机制: 当路由机制已经激活Controller并InvokeAction后,如果返回的是View, 则ViewResult基于View呈现的请求响应机制内部借助MVC提供的View引 ...

  3. 并发编程(七)——AbstractQueuedSynchronizer 之 CountDownLatch、CyclicBarrier、Semaphore 源码分析

    这篇,我们的关注点是 AQS 最后的部分,共享模式的使用.本文先用 CountDownLatch 将共享模式说清楚,然后顺着把其他 AQS 相关的类 CyclicBarrier.Semaphore 的 ...

  4. Chapter 5 Blood Type——9

    He grimaced. 他扮了一个鬼脸. "Or better," I continued, the pent-up annoyance flowing freely now, ...

  5. springboot+mybatis+dubbo+aop日志第三篇

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等. Spring AOP模块提供截取拦截应用程序的拦截器,例如,当执行方法时,可以在执行方法之前或之后添加 ...

  6. http服务器搭建之apache与nginx的特性

    一.了解一些简单的概念 HTTP服务器程序: httpd(apache) nginx lighttpd(功能和nginx差不多,活跃度不如ng) 应用程序服务器: IIS Tomcat(开源jsp容器 ...

  7. .Net语言 APP开发平台——Smobiler学习日志:获取或存储图像路径设置

    ResourcePath属性 一.属性介绍 获取或设置图像存储路径,默认设置为“image”,表示的ResourcePath是在程序运行路径下的Image文件夹(bin\Debug\Image): 该 ...

  8. 第28章 确认(Consent) - Identity Server 4 中文文档(v1.0.0)

    在授权请求期间,如果IdentityServer需要用户同意,则浏览器将被重定向到同意页面. 同意用于允许最终用户授予客户端对资源(身份或API)的访问权限.这通常仅对第三方客户端是必需的,并且可以在 ...

  9. 【转载】网站服务器运维记实:阿里云1核2G突发性能t5服务器突然变得卡顿

    阿里云突发性能服务器1核2G的t5服务器在高资源利用率的情况下运行一段时间后,发现服务器反应变得很慢,通过windows远程桌面连接上服务器后查看到CPU性能一直在90%到100%之间,无法降下来.前 ...

  10. 数据结构(java版)学习笔记(二)——线性表之顺序表

    顺序表的优点: 随机存取元素方便,根据定位公式容易确定表中每个元素的存储位置,所以要指定第i个结点很方便 简单,直观 顺序表的缺点: 插入和删除结点困难 扩展不灵活,难以确定分配的空间 容易造成浪费 ...