【问题标题】:RSA should I use X.509 or PKCS #1RSA 我应该使用 X.509 还是 PKCS #1
【发布时间】:2019-05-24 06:14:30
【问题描述】:

用例: 我有一个用例,其中客户端生成私钥和公钥,将 base 64 编码的公钥发送到服务器。

在服务器端,我将使用此公钥加密消息并将加密消息发送给客户端,客户端使用其私钥对其进行解密。约定的算法是“RSA”。

问题出在服务器端,我看到某些密钥正在使用 X509EncodedKeySpec 作为密钥规范

byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

虽然有些键使用 X509EncodedKeySpec 会引发异常 (Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence),但使用 RSAPublicKeySpec 可以工作:

byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey.getInstance(publicBytes);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

所以,我的理解是客户端和服务器需要同意是否使用: PKCS #1X.509 用于对密钥进行编码。 我的问题是哪一种更适合我的用例? 有什么指导方针何时使用哪种格式?

【问题讨论】:

  • RSAPublicKey,一个接口,没有静态的getInstance 方法。也没有办法意外地随机产生一种编码或另一种编码。
  • 使用 org.bouncycastle.asn1.pkcs.RSAPublicKey .. 编辑问题
  • 同样,也没有办法意外地随机产生一种编码或另一种编码。您必须使用与密钥编码方式兼容的方法。
  • 我的问题的最后一行是实际问题,使用 PKCS #1 还是 X.509 编码更好,有什么好处和坏处,什么时候使用哪个指南?
  • 我认为 X.509 比 PKCS#1 更广泛的规范(超集)。如您的示例所示, RSAPublicKey 接口需要模数和指数。从 X.509 证书中提取这些信息并不容易。

标签: java public-key-encryption public-key


【解决方案1】:

差别很小。 Java 调用 X.509 的密钥格式,更准确地称为 X.509 中定义的 ASN.1 结构 SubjectPublicKeyInfo(或 SPKI)或等效且更方便的定义在RFC5280 sec 4.1 中,是处理大量灵活算法集的一种非常简单的方法:它由一个子结构AlgorithmIdentifier 组成,它标识算法及其参数(如果适用),然后是一个包含实际密钥信息的不透明位字符串(编码)格式取决于算法标识符(由算法标识)。

对于 RSA,算法相关部分在 PKCS1 或更方便地定义在 RFC8017 appendix A.1.1 及其早期版本中的 ASN.1 结构 RSAPublicKey,并在 RFC3279 sec 2.3.1 中重复。因此对于 RSA,X.509 (SPKI) 格式包含 PKCS1 格式,并且由于 RSA 没有参数(或至少与密钥相关的参数),唯一真正的区别是 X. 509 格式明确指定密钥是 RSA - 在您的应用程序中您已经知道。

您已经发现 vanilla (Oracle-was-Sun-now-OpenJDK) Java 加密,又名 JCA Java 加密体系结构,仅直​​接支持 X.509 (SPKI) 格式,这是一个次要优势。但是,如果您使用 BouncyCastle,则来回转换比 Q 中的代码容易得多;您只需使用 org.bouncycastle.asn1.x509.SubjectPublicKeyInfo 类来添加或丢弃 AlgorithmIdentifier:

    // test data source
    KeyStore ks = KeyStore.getInstance("JKS"); ks.load (new FileInputStream (args[0]), args[1].toCharArray());
    byte[] spkienc = ks.getCertificate(args[2]).getPublicKey().getEncoded();
    System.out.println (DatatypeConverter.printHexBinary(spkienc));

    // extract PKCS1 part of original SPKI
    byte[] pkcs1enc = SubjectPublicKeyInfo.getInstance(spkienc).parsePublicKey().getEncoded();
    System.out.println (DatatypeConverter.printHexBinary(pkcs1enc));

    // rebuild SPKI from the PKCS1
    AlgorithmIdentifier algid = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
    byte[] spki2enc = new SubjectPublicKeyInfo (algid, pkcs1enc).getEncoded();
    System.out.println (DatatypeConverter.printHexBinary(spki2enc));

查看我对类似golang x509.MarshalPKIXPublicKey vs x509.MarshalPKCS1PublicKey() 的回答,尤其是以下链接:
Converting A public key in SubjectPublicKeyInfo format to RSAPublicKey format java
Generating RSA keys in PKCS#1 format in Java
Problem transmiting a RSA public key, javaME , bouncy castle

如果你没有 BouncyCastle,那就有点难了;您需要编写部分 ASN.1 解析器或生成器。完整的 ASN.1 处理相当复杂,但对于这种情况,您只需要一个还不错的小子集。 (是的,这是微弱的赞美。)如果我有更多的时间,我可能会在稍后添加。

一个更大的潜在问题是您的密钥未经过身份验证。 公钥分发的困难部分,比微小的格式细节更难,是确保分发合法密钥。如果攻击者可以用他们的公钥替换正确的公钥,那么受害者就会以攻击者可以轻松读取的方式加密所谓的秘密数据,而你所有花哨的密码学代码都完全没有价值。

这就是为什么大多数实际系统不分发裸公钥,而是分发允许验证密钥是正确密钥的证书的原因。有一些证书方案,但迄今为止最广泛使用的是 X.509 及其 Internet 配置文件 PKIX——实际上我上面提到的 RFC 5280 和 3279 是 PKIX 的一部分。 SSL-now-TLS 使用 X.509。代码签名使用 X.509。 S/MIME 电子邮件使用 X.509。 (PGP/GPG 使用不同类型的证书,不是 X.509,但仍然是证书。)并且(vanilla)Java 直接支持 X.509 证书,与“X.509”(SPKI)公钥一样甚至更好.

【讨论】:

    猜你喜欢
    • 2021-07-31
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 2013-07-20
    • 2011-09-19
    • 2014-06-30
    • 1970-01-01
    • 2015-02-26
    相关资源
    最近更新 更多