文章目录

  • 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公私钥的底层实现类

上面的RSAPublicKeyRSAPrivateKey只是方法接口,具体的底层实现类其实是在RSAPublicKeyImplRSAPrivateCrtKeyImpl中,它们的类结构图如下:

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之间的转换方法相关推荐

  1. java中使用openssl生成的rsa公私钥进行数据加解密_使用openssl生成RSA公钥和私钥对...

    在ubuntu上要使用openssl的话需要先进行安装,命令如下: sudo apt-get install openssl 安装完成就可以使用openssl了. 首先需要进入openssl的交互界面 ...

  2. 使用Java代码生成RSA公私钥的.pem文件

    大家好,我是神韵,是一个技术&生活博主.出文章目的主要是两个,一是好记忆不如烂笔头,记录总结中提高自己.二是希望我的文章可以帮到大家.欢迎大家留言讨论,你们的行动将是我无限的动力. 本篇主题是 ...

  3. Java(111):非对称加密RSA的使用(openssl生成RSA公私钥对)

    Java(111):非对称加密RSA的使用(openssl生成RSA公私钥对) 1.openssl生成RSA公私钥对 [root@loaclhost ~]# openssl version OpenS ...

  4. 妙借Git自带的OpenSSL生成RSA公私钥的.pem 文件

    大家好,我是神韵,是一个技术&生活博主.出文章目的主要是两个,一是好记忆不如烂笔头,记录总结中提高自己.二是希望我的文章可以帮到大家.欢迎来点赞打卡,你们的行动将是我无限的动力. 本篇主题是: ...

  5. ras私钥c#转java_RSA密钥,JAVA与.NET之间转换

    最近在做银联的一个接口,用到RSA签名,悲剧来了,.net用的RSA密钥格式和JAVA用的不一样 .net为XML格式 53KnujHcV0962zoLigW8d4AUb+1TS3LiySGrXhF5 ...

  6. Java中使用OpenSSL生成的RSA公私钥进行数据加解密

    本文出处:http://blog.csdn.net/chaijunkun/article/details/7275632,转载请注明.由于本人不定期会整理相关博文,会对相应内容作出完善.因此强烈建议在 ...

  7. Java OpenSSL生成的RSA公私钥进行数据加解密详细介绍

    最近用到企业微信向银行卡转账功能,因为需要使用到:标准RSA算法 故在网上了解一下相关的信息 SA是什么:RSA公钥加密算法是1977年由Ron Rivest.Adi Shamirh和LenAdlem ...

  8. RSA公私钥生成、加解密、签名及验签的原理及工具类

    目录 一.RSA简介 二.加密.签名区别 三.公私钥生成 四.RSA工具类 一.RSA简介 非对称加密算法,由一对密钥(公钥-私钥)来进行加密-解密.签名-验签的过程.公钥-私钥的生成与数学相关,算法 ...

  9. (PKCS1) RSA 公私钥 pem 文件 提取 公私钥 e d 和 模数 n

    参考链接:RSA公钥文件(PEM)解析 参考链接:OPENSSL中RSA私钥文件(PEM格式)解析[一] 参考:使用openssl命令剖析RSA私钥文件格式 RSA PKCS8 与 PKCS1 公私钥 ...

最新文章

  1. 七牛云中带前缀的路径斜杆是怎么写的_关于小程序上传图片到七牛的总结
  2. 蓝桥杯 人物相关性分析 二分
  3. SqlBulkCopy 批量复制数据到数据表
  4. Less中的命名空间
  5. Linux下使用SFTP命令
  6. 论文润色软件Stylewriter,whitesmoke,1check使用
  7. html页面缩小图片缩小,如何缩小html页面中的图片大小?
  8. 花了3个小时解决了和异地女朋友一起看电影的需求(内附源码)
  9. 安装CoffeeScript
  10. Android - 城市/单项/国家区号选择器基础使用 及 使用国际区号json文件
  11. Go: 内置net/http库解析
  12. 误删除文件怎么找回 数据恢复用这些方法
  13. 胖虎技术群Java后端的良师
  14. Blender SMPL-X 插件安装
  15. python多窗口传递信息_PyQT5 中两个界面之间数据传递
  16. 模式识别的一些基本概念
  17. 1231231312
  18. 周志明论架构之道:从SOA时代到微服务时代
  19. 第一章 TCP/IP 协议
  20. Flink的CheckPoint机制

热门文章

  1. 使用SQL Server创建表
  2. LocalDB安装和连接
  3. Himall商城支付事件、链接类型
  4. 未来 消费品企业 会员营销5大趋势
  5. javaWeb 11(三层架构)
  6. 信安实践——自建CA证书搭建https服务器
  7. 企业如何防范WebShell 攻击-CSDN公开课-专题视频课程
  8. 关于zuul的一些配置说明
  9. 1. Python_Django项目之大型电商项目介绍
  10. 5个理由告诉你, 区块链未必适合你的业务!