【问题标题】:How do we convert a String from PEM to DER format我们如何将字符串从 PEM 转换为 DER 格式
【发布时间】:2010-10-27 12:10:15
【问题描述】:

以以下格式发送字符串:

-----BEGIN RSA PUBLIC KEY-----
MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY
mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma
XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED
-----END RSA PUBLIC KEY-----

如何从这个字符串构造一个 PublicKey 对象? 已尝试以下 去掉页眉页脚和base64解码缓冲区

public static PublicKey getFromString(String keystr) throws Exception
  {
  //String S1= asciiToHex(keystr);
   byte[] keyBytes = new sun.misc.BASE64Decoder().decodeBuffer(keystr);
   X509EncodedKeySpec spec =
       new X509EncodedKeySpec(keyBytes);
     KeyFactory kf = KeyFactory.getInstance("RSA");
     return kf.generatePublic(spec);

  }

这会失败,要么是无效的密钥格式,要么会出现以下错误

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
 at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:188)
 at java.security.KeyFactory.generatePublic(KeyFactory.java:304)
 at PublicKeyReader.getFromString(PublicKeyReader.java:30)
 at Tst.main(Tst.java:36)

正在通过 openSSL 的 API PEM_write_bio_RSAPublicKey(bio, rsa); 生成密钥

【问题讨论】:

  • 链接已经用过,但是不会转der格式
  • 请注意,您尝试做的并不是真正的“转换为 DER”。转换为 DER 只是在这里解码 base64 并将其作为字节序列输出。您正在尝试解码 ASN.1 结构。

标签: java cryptography jce


【解决方案1】:

通过调用PEM_write_bio_RSAPublicKey,只有密钥模数和公共指数被编码到输出 PEM 数据中。但是 X509EncodedKeySpec 应该是这种 ASN.1 密钥格式:

 SubjectPublicKeyInfo ::= SEQUENCE {
   algorithm AlgorithmIdentifier,
   subjectPublicKey BIT STRING }

您应该使用 PEM_write_bio_PUBKEY 函数,该函数使用 SubjectPublicKeyInfo 结构对公钥进行编码,正如 X509EncodedKeySpec 所期望的那样

解码密钥的另一种可能的解决方案。不幸的是,我认为仅使用标准 JDK API 是不可能的,但可以使用 Bouncycastle 库来完成

import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;

public static PublicKey getFromString(String keystr) throws Exception
{
  //String S1= asciiToHex(keystr);
   byte[] keyBytes = new sun.misc.BASE64Decoder().decodeBuffer(keystr);
   ASN1InputStream in = new ASN1InputStream(keyBytes);
   DERObject obj = in.readObject();
   RSAPublicKeyStructure pStruct = RSAPublicKeyStructure.getInstance(obj);
   RSAPublicKeySpec spec = new RSAPublicKeySpec(pStrcut.getModulus(), pStruct.getPublicExponent());
   KeyFactory kf = KeyFactory.getInstance("RSA");
   return kf.generatePublic(spec);
}

【讨论】:

  • 应该如何或在哪里指定 SubjectPublicKeyInfo 结构?
  • SubjectPublicKeyInfo 结构在 RFC 5280 (tools.ietf.org/html/rfc5280) 中指定,公钥结构在 RFC 3279 (tools.ietf.org/html/rfc3279) 中指定
  • 收到客户端密钥,因此无法更改格式。您能否建议任何其他方式来加载此密钥并通过 JCE 示例获取详细信息。
  • 您能否建议是否可以将使用 keypair.getPublic.getEncoded() 生成的公钥与 openSSL api 调用 BIO_write 和 PEM_read_bio_RSAPublicKey 一起使用
  • 我不这么认为。 PublicKey 上的 getEncoded() 返回一个编码的 SubjectPublicKeyInfo 并且 PEM_read_bio_RSAPublicKey 需要一个 PKCS#1 RSAPublicKey 结构(即只有模数和公共指数:与您尝试转换的密钥相同的结构)。根据 OpenSSL 文档,PEM_read_bio_RSA_PUBKEY 可能有效。
【解决方案2】:

BouncyCastle 的 PEMReader 将为您执行此操作:

String pemKey = "-----BEGIN RSA PUBLIC KEY-----\n"
            + "MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY\n"
            + "mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma\n"
            + "XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED\n"
            + "-----END RSA PUBLIC KEY-----\n";
PEMReader pemReader = new PEMReader(new StringReader(pemKey));
RSAPublicKey rsaPubKey = (RSAPublicKey) pemReader.readObject();
System.out.println("Public key: "+rsaPubKey);

(请注意,您之前可能需要Security.addProvider(new BouncyCastleProvider());。)

【讨论】:

  • 我在 android 中执行此操作。我无法导入 PEMReader。在依赖项中添加了 compile 'org.bouncycastle:bcprov-jdk15on:1.56'。
  • @madhuriHR:BouncyCastle 在 1.47 和 1.50 (2012-2013) 之间重构了他们的一些架构,现在这个功能在 PEMParserJcaPEMKeyConverter 之间拆分——参见 stackoverflow.com/questions/41421154/… 或 Alain ODea 的回答(包括我的评论)。
【解决方案3】:

更新:大大感谢@dave_thompson_085

,简化了流程和代码

您可以从您提供的字符串构造一个 PublicKey 对象,如下所示:

  1. 从二进制 DER 中读取主题公钥信息 (SPKI)(使用 Bouncy Castle 的 PEMParser)
  2. 将 SPKI 输入转换器以获取 PublicKey(Bouncy's Castle 的 JcaPEMKeyConverter 有效)

我的解决方案的先决条件:

  1. Java 7+(或者您需要手动展开 try-with-resources)
  2. Bouncy Castle bcprov-jdk15on 1.51 或更高版本(不在 1.50 或更早版本上运行,无法在 1.47 或更早版本上编译)

完整的 Java 7+ 示例:

import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

import java.io.IOException;
import java.io.StringReader;
import java.security.PublicKey;

public interface PemToDer
{
    static void main(String[] args) throws IOException {
        createRsaPublicKey(
                "-----BEGIN RSA PUBLIC KEY-----\n" +
                "MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY\n" +
                "mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma\n" +
                "XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED\n" +
                "-----END RSA PUBLIC KEY-----"
        );
    }

    static PublicKey createRsaPublicKey(String keystr) throws IOException {
        try (StringReader reader = new StringReader(keystr);
             PEMParser pemParser = new PEMParser(reader)) {
            SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) pemParser.readObject();
            JcaPEMKeyConverter pemKeyConverter = new JcaPEMKeyConverter();
            return pemKeyConverter.getPublicKey(subjectPublicKeyInfo);
        }
    }
}

【讨论】:

  • 如果你使用子类PEMParser而不是PemReader,它会返回一个SubjectPublicKeyInfo结构,你可以将它的编码作为X509EncodedKeySpec传递给密钥工厂——或者你可以使用JcaPEMKeyConverter为你做后者。
  • 哇。这干净多了。谢谢你。即将编辑。
猜你喜欢
  • 2014-02-06
  • 2018-01-04
  • 1970-01-01
  • 2012-04-02
  • 2020-01-28
  • 2016-07-15
  • 2020-01-18
  • 2017-07-01
  • 2018-07-30
相关资源
最近更新 更多