RSA公私钥格式分析及其在Java和Openssl之间的转换方法
文章目录
- PKCS#1和PKCS#8
- X.509公钥证书
- ASN.1抽象语法标记
- DER和PEM编码
- OID对象标识符
- 用openssl命令生成PKCS1#格式的RSA密钥对
- 生成私钥
- 从私钥中导出公钥
- PKCS#1格式的RSA公钥
- PKCS#1格式的RSA私钥
- PKCS#8格式的RSA私钥(未加密)
- PKCS#8格式的RSA公钥 与 X.509中RSA公钥
- Java中的RSA公私钥
- RSAPublicKey 方法接口
- RSAPrivateKey 方法接口
- RSA公私钥的底层实现类
- Java与Openssl的RSA公私钥的转换
- Java中Key的getEncoded()与密钥重构
- Java与Openssl的RSA公私钥的转换
- 待续
本文主要描述了RSA私钥的PKCS#1与PKCS#8编码,RSA公钥的PKCS#1与X.509编码,以及RSA公私钥在Openssl和Java之间的转换。
PKCS#1和PKCS#8
PKCS(The Public-Key Cryptography Standards)是一系列公钥密码学的相关标准,其中 《PKCS#1: RSA Cryptography Standard》 是RSA密码学标准的基础部分,而 《PKCS#8: Private-Key Information Syntax Standard》 是私钥信息语法格式。
在PKCS#1中,详细地定义了RSA公钥和私钥的语法格式,通常将符合这种语法的公私钥称为PKCS#1格式的公私钥。
在PKCS#8中,描述了一种私钥信息的语法,同时还提供一种加密密钥的语法。比如RSA私钥证书中的加密私钥就是用此描述的。相应地,符合此标准的私钥也称为PKCS#8格式的私钥。
通常,在java中的RSA私钥是使用PKCS#8格式,在Openssl中是PKCS#1格式。
X.509公钥证书
X.509是一系列的证书标准。在这里提及是因为Java中的RSA公钥的编码就是符合X.509证书公钥的。通常,Openssl中的公钥采用PKCS#1编码,而Java中则通常是X.509编码。
ASN.1抽象语法标记
ASN.1描述了一种对数据进行表示、编码、传输和解码的数据格式。 一般都采用ASN.1来定义一个通用的、抽象的数据结构。PKCS系列标准中也是如此,在PKCS#1标准的附录A可以看到RSA公私钥的ASN.1的详细描述;在PKCS#8中可以看到私钥(加密私钥)信息语法的ASN.1的详细描述。
ASN.1中常见的数据类型定义如下:
类型码 | ASN.1类型 | 作用 |
---|---|---|
1 | 布尔型 | 储存布尔值 |
2 | 整数 | 储存大整数 |
3 | 位串 | 存储位数组 |
4 | 八位位串 | 存储字节数组 |
5 | 空 | 预留位 |
6 | 对象标识符 | 标识算法及协议 |
DER和PEM编码
证书和密钥的常见编码有DER和PEM两种。
- DER(Distinguished Encoding Rules)可辨别编码规则是BER的一个子集。而BER基本编码规则,是指在 ASN.1 标准中描述的数据编码/解码规则。
- PEM编码就是把DER编码的数据用Base64进行编码,再加上开始和结束行,如证书文件的"-----BEGIN CERTIFICATE-----“和”-----END CERTIFICATE-----"。
DER编码的数据是用二进制表示的,直接查看只能看到一些乱码。而PEM是Base64编码的,所以可以直接查看。
OID对象标识符
在证书和密钥等结构的ASN.1描述中, 通常需要用OID来标识一个对象。比如标识一个私钥是RSA私钥或标识一个哈希算法是SHA1算法等待。在ASN.1中定义了这种数据类型,它的标识代码是06。
用openssl命令生成PKCS1#格式的RSA密钥对
在分析之前,我们先来看看用Openssl命令怎么来生成RSA密钥。
生成私钥
openssl genrsa -out prikey.pem 1024
上面这条命令可以生成一个PKCS#1格式的,PEM编码的,1024位的RSA私钥。用NotePad++打开查看:
从私钥中导出公钥
openssl rsa –in prikey.pem –RSAPublicKey_out –out pubkey.pkcs1.pem
这条命令可以从RSA私钥中PKCS#1格式的、PEM编码的RSA公钥。用NotePad++打开查看:
PKCS#1格式的RSA公钥
PKCS#1格式的RSA公钥的ASN.1描述如下:
RSAPublicKey ::= SEQUENCE {modulus INTEGER, -- npublicExponent INTEGER -- e
}
其中:
- modulus: 是RSA模数,是一个正整数
- publicExponent: 是RSA公钥幂指数,是一个正整数
我们用Asn1View来查看一下前面生成的pubkey.pkcs1.pem这个RSA公钥:
可以看到,它是符合PKCS标准的。其中包括了n和e这两个RSA公钥元素。其中e为65537,即0x10001,这是一个最常用的公钥幂指数。
RSA算法中的公钥指数目前在用的有两个,其一名为F0,等于3;其二名为F4,等于65537。
PKCS#1格式的RSA私钥
PKCS#1格式的RSA私钥的ASN.1描述如下:
RSAPrivateKey ::= SEQUENCE {version Version, modulus INTEGER, -- npublicExponent INTEGER, -- eprivateExponent INTEGER, -- dprime1 INTEGER, -- pprime2 INTEGER, -- qexponent1 INTEGER, -- d mod (p-1)exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod potherPrimeInfos OtherPrimeInfos OPTIONAL
}
其中各字段含义如下:
- version:版本号,为了与本文档的今后版本兼容。V2中这个版本号应该是0,但如果使用了多素数,则版本号应该是1。
Version ::= INTEGER { two-prime(0), multi(1) }(CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})
- modulus:RSA的模数n
- publicExponent:RSA的公钥幂指数e
- privateExponent:RSA的私钥幂指数d
- prime1:n的素因子p
- prime2:n的素因子q
- exponent1:=d mod (p-1)
- exponent2:=d mod (q-1)
- coefficient:=q-1 mod p,代表CRT(中国剩余定理)系数,
- otherPrimeInfos:按顺序包含了其他的素数“r3,……ru”。如果version是0,则它被忽略;如果version是1,那么它至少应该包含一个OtherPrimeInfo实例。OtherPrimeInfo的定义如下:
OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
OtherPrimeInfo的ASN.1结构如下:
OtherPrimeInfo ::= SEQUENCE {prime INTEGER, -- riexponent INTEGER, -- dicoefficient INTEGER -- ti
}
OtherPrimeInfo中各字段含义如下:
- prime:n的素因子ri,where i >= 3
- exponent:di = d mod (ri - 1)
- coefficient:ti = (r1 · r2 · … · ri–1)-1 mod ri
我们用Asn1View来查看一下前面生成的prikey.pem这个RSA私钥:
从上面的PKCS#1的RSA私钥结构中我们可以知道,RSA公钥被包含于其中,所以在前面我们才可以用Openssl命令从RSA私钥中导出公钥。
PKCS#8格式的RSA私钥(未加密)
在PKCS#8标准中给出的私钥信息(未加密)的语法格式如下:
PrivateKeyInfo ::= SEQUENCE {verion VersionprivateKeyAlgorithm PrivateKeyAlgorithmIdentifierprivateKey PrivateKeyattributes [0] IMPLICIT Attributes OPTIONAL
}Version ::= INTEGERPrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifierPrivateKey ::= OCTET STRINGAttributes ::= SET OF AttributeAlgorithmIdentifier ::= SEQUENCE {algorithm OBJECT IDENTIFIER,parameters ANY DEFINED BY algorithm OPTIONAL
}
其中个字段含义为:
- version:版本号,目前为0
- privateKeyAlgorithm:私钥算法标识
- privateKey:私钥字节串,内容为私钥的值
- attributes:属性集。这些属性是连同私钥信息一起被加密的扩展信息
我们使用下面的Openssl命令将前面的PKCS#1编码的RSA私钥转为PKCS#8编码的RSA私钥:
openssl pkcs8 -topk8 -in prikey.pem -out prikey.pkcs8.pem -nocrypt
然后用Asn1View来查看一下:
在这个PKCS#8编码的RSA私钥中,私钥算法OID为1.2.840.113549.1.1.1,其中1.2.840.113549.1.1是PKCS1标准的OID,它下面有很多分支,第一个分支标识RSA私钥。其中的privateKey这个私钥数据就是一个PKCS#1编码的RSA私钥的BER编码。
PKCS#8格式的RSA公钥 与 X.509中RSA公钥
在X.509公钥证书中定义的公钥信息如下:
SubjectPublicKeyInfo ::= SEQUENCE {algorithm AlgorithmIdentifier,subjectPublicKey BIT STRING
}AlgorithmIdentifier ::= SEQUENCE {algorithm OBJECT IDENTIFIER,parameters ANY DEFINED BY algorithm OPTIONAL
}
其中:
- algorithm:公钥算法标识
- subjectPublicKey:公钥数据
我们使用下面的Openssl命令将前面的PKCS#1编码的RSA公钥钥转为X509中的RSA公钥:
openssl rsa –in prikey.pem -pubout -out pubkey.x509.pem
然后用Asn1View来查看一下:
在这个X.509的RSA公钥中,公钥算法OID为1.2.840.113549.1.1.1,表示是一个PKCS#1格式的公钥。其中的subjectPublicKey这个公钥数据就是一个PKCS#1编码的RSA公钥的BER编码。
PKCS#8公钥
我们可以从X.509的RSA公钥中看到,它的ASN.1结构描述和PKCS#8中的私钥信息语法基本相同,都是一个算法OID加上密钥数据。所以有时候我们可能也简便地将X.509公钥称为PKCS#8格式的公钥,这样可以将公私钥与PKCS#1和PKCS#8一同提及。但应该明白,PKCS#8标准中并没有定义公钥消息语法,它是定义在X.509公钥证书标准中的。
Java中的RSA公私钥
RSAPublicKey 方法接口
Java中的RSA公钥接口RSAPublicKey的结构图如下:
RSAPrivateKey 方法接口
Java中的RSA私钥接口RSAPrivateKey的结构图如下:
可以从Java中RSA公私钥的接口方法中看到,公私钥只包括了必要的2个元素,即模数和幂指数,因为RSA算法的基本计算模型就是:
输入数据.modPow(幂指数, 模数)
其中输入数据、幂指数和模数都是BigInteger类型。
RSA公私钥的底层实现类
上面的RSAPublicKey和RSAPrivateKey只是方法接口,具体的底层实现类其实是在RSAPublicKeyImpl和RSAPrivateCrtKeyImpl中,它们的类结构图如下:
从RSAPrivateCrtKeyImpl的源码中可以看到,该结构包含了在PKCS#1中定义的RSA私钥的除otherPrimeInfos之外的所有字段;同时继承自PKCS8Key,它实现了RSA私钥的PKCS8编码。
从RSAPublicKeyImpl的源码中也可以看到,该结构包含了在PKCS#1中定义的RSA公钥结构的所有元素;同时继承自X509Key,它实现了RSA公钥的X509编码。
Java与Openssl的RSA公私钥的转换
在Java和Openssl中互用RSA公私钥时,主要涉及到RSA私钥的PKCS#1与PKCS#8的相互转换、RSA公钥的PKCS#1和X509格式之间的转换、DER和PEM编码之间的转换,以及Java中通过RSA密钥字节数组构造RSAPublicKey和RSAPrivateKey结构的方法。
注意:这里涉及到的RSA私钥都是没有加密的。
Java中Key的getEncoded()与密钥重构
这里的字节数组是指从RSAPublicKey和RSAPrivateKey的getEncoded方法获取的数组,这样才可以使用下面的方法将其还原。
public static RSAPrivateKey genPrivateKey(byte[] derPrikeyPkcs8Encoded) throws Exception{PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(derPrikeyPkcs8Encoded);KeyFactory keyFactory = KeyFactory.getInstance("RSA");return (RSAPrivateKey)keyFactory.generatePrivate(keySpec);}public static RSAPublicKey genPublicKey(byte[] derPubkeyX509Encoded) throws Exception{X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derPubkeyX509Encoded);KeyFactory keyFactory = KeyFactory.getInstance("RSA");return (RSAPublicKey)keyFactory.generatePublic(keySpec);}
Java与Openssl的RSA公私钥的转换
对于Openssl和Java之间的RSA公私钥的转换,这里基于BouncyCastle库提供一种转换的实现方式,代码如下:
package com.yy.rsa;import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.security.Key;/*** RSA密钥格式转换.** 私钥的PKCS#1和PKCS#8之间的转换.* 公钥的PKCS#1和X509之间的转换.** PKCS1编码的密钥的PEM格式的前缀带有算法名称;非PKCS1的则没有.** Created by YaoYuan on 2019/4/30.*/
public class RsaKeyCodec {public static final String TYPE_PREFIX_PKCS1_PRIVATE_KEY = "RSA PRIVATE KEY";public static final String TYPE_PREFIX_PKCS1_PUBLIC_KEY = "RSA PUBLIC KEY";public static final String TYPE_PREFIX_PKCS8_PRIVATE_KEY = "PRIVATE KEY";public static final String TYPE_PREFIX_X509_PUBLIC_KEY = "PUBLIC KEY";/*** DER转PEM** @param typePrefix 密钥类型前缀,如"PRIVATE KEY".* 参考类型:* {@link #TYPE_PREFIX_PKCS1_PRIVATE_KEY},* {@link #TYPE_PREFIX_PKCS1_PUBLIC_KEY},* {@link #TYPE_PREFIX_PKCS8_PRIVATE_KEY},* {@link #TYPE_PREFIX_X509_PUBLIC_KEY}* @param derKeyEncoded 编码后的密钥数据* @return PKCS8编码的PEM*/public static String derToPem(String typePrefix, byte[] derKeyEncoded) throws Exception {PemObject pemObject = new PemObject(typePrefix, derKeyEncoded);StringWriter stringWriter = new StringWriter();PemWriter pemWriter = new PemWriter(stringWriter);pemWriter.writeObject(pemObject);pemWriter.close();return stringWriter.toString();}/*** PEM转DER** @param pemKey PEM的key* @return DER字节数组*/public static byte[] pemToDer(String pemKey) throws IOException {InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(pemKey.getBytes()));PemReader pemReader = new PemReader(isr);return pemReader.readPemObject().getContent();}/*** PKCS8编码的私钥转为PKCS1,DER格式.** @param derPrikeyPkcs8Encoded 编码后的PKCS8私钥数据,通过java的{@link Key#getEncoded()}获取* @return PKCS1编码的DER私钥*/public static byte[] der_prikey_pkcs8ToPkcs1(byte[] derPrikeyPkcs8Encoded) throws Exception {PrivateKeyInfo pki = PrivateKeyInfo.getInstance(derPrikeyPkcs8Encoded);org.bouncycastle.asn1.pkcs.RSAPrivateKey rsaPrivateKey = org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(pki.parsePrivateKey());return rsaPrivateKey.getEncoded();}/*** X509编码的公钥转为PKCS1,DER格式.** @param derPubkeyX509Encoded 编码后的X509公钥数据,通过java的{@link Key#getEncoded()}获取* @return PKCS1编码的DER公钥*/public static byte[] der_pubkey_x509ToPkcs1(byte[] derPubkeyX509Encoded) throws Exception {SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(derPubkeyX509Encoded);org.bouncycastle.asn1.pkcs.RSAPublicKey rsaPublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(spki.parsePublicKey());return rsaPublicKey.getEncoded();}/*** PKCS1编码的私钥转为PKCS8,DER格式.** @param derPrikeyPkcs1Encoded 编码后的PKCS1私钥数据* @return PKCS8编码的DER私钥*/public static byte[] der_prikey_pkcs1ToPkcs8(byte[] derPrikeyPkcs1Encoded) throws Exception {AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag);ASN1Object asn1Object = ASN1Primitive.fromByteArray(derPrikeyPkcs1Encoded);PrivateKeyInfo privateKeyInfo = new PrivateKeyInfo(algorithmIdentifier, asn1Object);return privateKeyInfo.getEncoded();}/*** PKCS1编码的公钥转为X509,DER格式.** @param derPubkeyPkcs1Encoded 编码后的PKCS1公钥数据* @return X509编码的DER公钥*/public static byte[] der_pubkey_pkcs1ToX509(byte[] derPubkeyPkcs1Encoded) throws Exception {AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag);ASN1Object asn1Object = ASN1Primitive.fromByteArray(derPubkeyPkcs1Encoded);SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo(algorithmIdentifier, asn1Object);return spki.getEncoded();}
}
待续
RSA公私钥格式分析及其在Java和Openssl之间的转换方法相关推荐
- java中使用openssl生成的rsa公私钥进行数据加解密_使用openssl生成RSA公钥和私钥对...
在ubuntu上要使用openssl的话需要先进行安装,命令如下: sudo apt-get install openssl 安装完成就可以使用openssl了. 首先需要进入openssl的交互界面 ...
- 使用Java代码生成RSA公私钥的.pem文件
大家好,我是神韵,是一个技术&生活博主.出文章目的主要是两个,一是好记忆不如烂笔头,记录总结中提高自己.二是希望我的文章可以帮到大家.欢迎大家留言讨论,你们的行动将是我无限的动力. 本篇主题是 ...
- Java(111):非对称加密RSA的使用(openssl生成RSA公私钥对)
Java(111):非对称加密RSA的使用(openssl生成RSA公私钥对) 1.openssl生成RSA公私钥对 [root@loaclhost ~]# openssl version OpenS ...
- 妙借Git自带的OpenSSL生成RSA公私钥的.pem 文件
大家好,我是神韵,是一个技术&生活博主.出文章目的主要是两个,一是好记忆不如烂笔头,记录总结中提高自己.二是希望我的文章可以帮到大家.欢迎来点赞打卡,你们的行动将是我无限的动力. 本篇主题是: ...
- ras私钥c#转java_RSA密钥,JAVA与.NET之间转换
最近在做银联的一个接口,用到RSA签名,悲剧来了,.net用的RSA密钥格式和JAVA用的不一样 .net为XML格式 53KnujHcV0962zoLigW8d4AUb+1TS3LiySGrXhF5 ...
- Java中使用OpenSSL生成的RSA公私钥进行数据加解密
本文出处:http://blog.csdn.net/chaijunkun/article/details/7275632,转载请注明.由于本人不定期会整理相关博文,会对相应内容作出完善.因此强烈建议在 ...
- Java OpenSSL生成的RSA公私钥进行数据加解密详细介绍
最近用到企业微信向银行卡转账功能,因为需要使用到:标准RSA算法 故在网上了解一下相关的信息 SA是什么:RSA公钥加密算法是1977年由Ron Rivest.Adi Shamirh和LenAdlem ...
- RSA公私钥生成、加解密、签名及验签的原理及工具类
目录 一.RSA简介 二.加密.签名区别 三.公私钥生成 四.RSA工具类 一.RSA简介 非对称加密算法,由一对密钥(公钥-私钥)来进行加密-解密.签名-验签的过程.公钥-私钥的生成与数学相关,算法 ...
- (PKCS1) RSA 公私钥 pem 文件 提取 公私钥 e d 和 模数 n
参考链接:RSA公钥文件(PEM)解析 参考链接:OPENSSL中RSA私钥文件(PEM格式)解析[一] 参考:使用openssl命令剖析RSA私钥文件格式 RSA PKCS8 与 PKCS1 公私钥 ...
最新文章
- 七牛云中带前缀的路径斜杆是怎么写的_关于小程序上传图片到七牛的总结
- 蓝桥杯 人物相关性分析 二分
- SqlBulkCopy 批量复制数据到数据表
- Less中的命名空间
- Linux下使用SFTP命令
- 论文润色软件Stylewriter,whitesmoke,1check使用
- html页面缩小图片缩小,如何缩小html页面中的图片大小?
- 花了3个小时解决了和异地女朋友一起看电影的需求(内附源码)
- 安装CoffeeScript
- Android - 城市/单项/国家区号选择器基础使用 及 使用国际区号json文件
- Go: 内置net/http库解析
- 误删除文件怎么找回 数据恢复用这些方法
- 胖虎技术群Java后端的良师
- Blender SMPL-X 插件安装
- python多窗口传递信息_PyQT5 中两个界面之间数据传递
- 模式识别的一些基本概念
- 1231231312
- 周志明论架构之道:从SOA时代到微服务时代
- 第一章 TCP/IP 协议
- Flink的CheckPoint机制