如何将一个PEM格式的字符串转换为DER格式

15

有一个字符串以以下格式发送:

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

我应该如何从这个字符串构建一个PublicKey对象? 尝试了以下步骤: 去掉头和尾,对缓冲区进行base64解码

How do i construct a PublicKey Object from this string ? Have tried the below Remove the header and footer and base64 decode the buffer

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格式。 - MSSV
请注意,您尝试做的并不是真正的“转换为DER”。将其转换为DER只是解码此处的base64并将其输出为字节序列。您正在尝试解码ASN.1结构。 - Bruno
3个回答

9
通过调用PEM_write_bio_RSAPublicKey,只有密钥模数和公共指数被编码到输出的PEM数据中。然而,X509EncodedKeySpec期望这种ASN.1密钥格式:
 SubjectPublicKeyInfo ::= SEQUENCE {
   algorithm AlgorithmIdentifier,
   subjectPublicKey BIT STRING }

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

解码密钥的另一种可能的解决方案。不幸的是,我认为只使用标准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结构应该在哪里或如何指定? - MSSV
SubjectPublicKeyInfo结构在RFC 5280(http://tools.ietf.org/html/rfc5280)中进行了规定,公钥结构在RFC 3279(http://tools.ietf.org/html/rfc3279)中进行了规定。 - Jcs
请问您是否可以建议一下,是否可以使用通过keypair.getPublic.getEncoded()生成的publickey与openSSL api调用BIO_write和PEM_read_bio_RSAPublicKey配合使用? - MSSV
我认为不行。PublicKey的getEncoded()方法返回一个编码后的SubjectPublicKeyInfo,而PEM_read_bio_RSAPublicKey期望的是PKCS#1 RSAPublicKey结构(即仅包含模数和公共指数:与您尝试转换的密钥相同的结构)。根据OpenSSL文档,PEM_read_bio_RSA_PUBKEY可能有效。 - Jcs
代码无法编译。ASNASN1InputStream不存在。 - Alain O'Dea
显示剩余3条评论

6
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'。 - madhuri H R
@madhuriHR:BouncyCastle在1.47和1.50(2012-2013)之间重构了一些架构,现在这个功能被分成了PEMParserJcaPEMKeyConverter--请参见https://stackoverflow.com/questions/41421154/unable-to-verify-string-signed-by-openssl-with-dsa-key或Alain ODea的答案(包括我的评论)。 - dave_thompson_085

1

更新:感谢 @dave_thompson_085 的帮助,流程和代码已经被极大地简化。

您可以通过以下方式将提供的字符串构建为 PublicKey 对象:

  1. 从二进制 DER 中读取 Subject Public Key Information(SPKI)(使用 Bouncy Castle 的 PEMParser)
  2. 将 SPKI 提供给转换器以获取 PublicKey(Bouncy 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);
        }
    }
}

1
如果您使用子类PEMParser而不是PemReader,它会返回一个SubjectPublicKeyInfo结构,您可以将其编码作为X509EncodedKeySpec传递给密钥工厂--或者您可以使用JcaPEMKeyConverter来为您执行后者。 - dave_thompson_085
哇,这干净多了。谢谢你。我马上就编辑。 - Alain O'Dea
这个方法pemKeyConverter.getPublicKey(subjectPublicKeyInfo)的返回类型是DER吗? - Shadow
@Shadow pemKeyConverter.getPublicKey(subjectPublicKeyInfo) 的返回类型是 PublicKey,因此 createRsaPublicKey 的返回类型也必须是 PublicKey,否则会出现类型错误。请参见 Javadoc:https://www.bouncycastle.org/docs/pkixdocs1.8on/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.html#getKeyPair-org.bouncycastle.openssl.PEMKeyPair-。请参见源代码:https://github.com/bcgit/bc-java/blob/9032821b326fba9fec7528899dfffa79f9f19aef/pkix/src/main/java/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.java#L71 - Alain O'Dea
请告诉我如何转换成DER格式? - Shadow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接