遇到的问题

在一个与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. node开发备注

    设置环境变量 // 命令行启动: "scripts": { "start": "export NODE_ENV=dev && node ...

  2. Hive 导入 parquet 格式数据

    Hive 导入 parquet 数据步骤如下: 查看 parquet 文件的格式 构造建表语句 倒入数据 一.查看 parquet 内容和结构 下载地址 社区工具 GitHub 地址 命令 查看结构: ...

  3. Jvm垃圾回收器(算法篇)

    在<Jvm垃圾回收器(基础篇)>中我们主要学习了判断对象是否存活还是死亡?两种基础的垃圾回收算法:引用计数法.可达性分析算法.以及Java引用的4种分类:强引用.软引用.弱引用.虚引用.和 ...

  4. Python SQLalchemy的学习与使用

    SQLAlchemy是python中最著名的ORM(Object Relationship Mapping)框架了. 前言:什么是ORM? ORM操作是所有完整软件中后端处理最重要的一部分,主要完成了 ...

  5. Java基础之 数组详解

    前言:Java内功心法之数组详解,看完这篇你向Java大神的路上又迈出了一步(有什么问题或者需要资料可以联系我的扣扣:734999078) 数组概念 同一种类型数据的集合.其实数组就是一个容器. 数组 ...

  6. Linux之安装常用软件

    Linux下安装软件的方法: 1,rpm(不推荐使用) 2,yum安装(使用快捷方便) 3,编译安装 一.安装python3(这里使用的是编译安装) 1,下载python3源码包 在centos下,第 ...

  7. DSAPI多功能组件编程应用-HTTP监听服务端与客户端

    本文中,演示了使用DSAPI.网络相关.HTTP监听,快速建立服务端和客户端. HTTP监听服务端的作用,是监听指定计算机端口,以实现与IIS相同的解析服务,提供客户端的网页请求,当然,这不仅仅是应用 ...

  8. 【转】NotificationCopat.Builder全部设置

    1.方法:setContentTitle(CharSequence title)   功能:设置通知栏标题.   例子:setContentTitle("测试标题"). 2.方法: ...

  9. Android开发过程中的坑及解决方法收录(三)

    bug:应用出现了 不幸运的,应用已停止的错误提示 排除问题: 1.intent接收数据的字符串不匹配 2.java常见的NullPointerException(空指针错误),可能由三个原因引起,字 ...

  10. 永不重复的id生成器

    目录 (1)需要导入的包 (2)IdGenerator类 (3)使用举例 (1)需要导入的包 主要用在格式化日FastDateFormat.getInstance("yyyyMMddHHmm ...