【问题标题】:Create PrivateKey and PublicKey from a String base64 encoding with DER format从 DER 格式的字符串 base64 编码创建 PrivateKey 和 PublicKey
【发布时间】:2012-01-17 06:54:21
【问题描述】:

我的私钥和公钥在 base64 的字符串中,使用 ANS1 DER 编码。我尝试创建 java PrivateKeyPublicKey 的实例:

byte [] llave2 = DatatypeConverter.parseBase64Binary(key);
PKCS8Key pkcs8 = new PKCS8Key( llave2, password.toCharArray()); //line 2
llave2 = pkcs8.getDecryptedBytes();                             //line 3
certificado = DatatypeConverter.parseBase64Binary(cer);

KeyFactory kf = KeyFactory.getInstance("RSA");  
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(llave2);
PrivateKey privateKey = kf.generatePrivate(ks);
X509EncodedKeySpec x = new X509EncodedKeySpec(certificado);
PublicKey publicKey = kf.generatePublic(x);

我在PublicKey publicKey = kf.generatePublic(x) 中收到以下错误。

    java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException:     IOException: ObjectIdentifier() -- data isn't an object ID (tag = -96)
    at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(Unknown Source)
    at java.security.KeyFactory.generatePublic(Unknown Source)
    at vital.cancelaciones.GeneraXMLCancelacion.main(GeneraXMLCancelacion.java:118)
Caused by: java.security.InvalidKeyException: IOException: ObjectIdentifier() -- data isn't an object ID (tag = -96)
    at sun.security.x509.X509Key.decode(Unknown Source)
    at sun.security.x509.X509Key.decode(Unknown Source)
    at sun.security.rsa.RSAPublicKeyImpl.<init>(Unknown Source)
    at sun.security.rsa.RSAKeyFactory.generatePublic(Unknown Source)
    ... 3 more

我想我应该对第 2 行和第 3 行中的私钥做类似的处理。因为证书也是加密的。有什么建议吗?

【问题讨论】:

    标签: java encryption private-key der


    【解决方案1】:

    为了测试您的场景,我使用openssl 创建了一个 RSA 私钥。

    openssl genrsa -out private.pem 1024
    

    然后我已将此密钥转换为 PKCS#8 DER 格式。

    openssl pkcs8 -topk8 -inform PEM -in private.pem -outform DER -out private.der -nocrypt
    

    openssl 的手册将 PKCS#8 和 DER 都称为格式,所以就我而言,会发生以下情况:

    • pkcs8 告诉 openssl 我想使用 PKCS#8 格式的私钥。
    • -topk8 告诉它我要用 -in 指定的私钥在 PKCS#8 中是 not(否则它会假定它是)。
    • -inform-in 指定我想将 (PEM) 私钥转换为 PKCS#8(没有 -topk8 它会尝试将已采用 PKCS#8 格式的密钥转换为 标准 em> 键格式)。
    • -outform-out 告诉它我想要一个 DER 格式的密钥作为输出。
    • -nocrypt 告诉它我不想加密密钥。

    然后,我用我的 RSA 密钥(标准格式)创建了一个证书。

    openssl req -new -x509 -keyform PEM -key private.pem -outform DER -out public.der
    

    证书包含与我的私钥对应的公钥。

    在所有这些之后,我已经使用 Base64 对私钥和证书进行了编码。

    base64 private.der > private.der.b64
    base64 public.der > public.der.b64
    

    生成了以下文件。

    private.pem      # standard
    private.der      # pkcs8/DER
    private.der.b64 
    public.der       # x509/DER
    public.der.b64   
    
    public static void main(String[] args) throws IOException, GeneralSecurityException {
      // get a handle on the base64 encoded key and certificate
      File privateKeyFile = new File("private.der.b64");
      File publicKeyFile = new File("public.der.b64");
    
      // pull them into arrays
      byte[] privateKeyBytes = toByteArray(privateKeyFile);
      byte[] publicKeyBytes = toByteArray(publicKeyFile);
    
      // decode them
      privateKeyBytes = toDecodedBase64ByteArray(privateKeyBytes);
      publicKeyBytes = toDecodedBase64ByteArray(publicKeyBytes);
    
      // get the private key
      KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      KeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
      PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
    
      // get the public key
      CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
      Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(publicKeyBytes));
      PublicKey publicKey = certificate.getPublicKey();
    }
    
    private static byte[] toByteArray(File file) throws IOException {
      // java 7's try-with-resources statement
      try (FileInputStream in = new FileInputStream(file);
          FileChannel channel = in.getChannel()) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        channel.transferTo(0, channel.size(), Channels.newChannel(out));
        return out.toByteArray();
      }
    }
    
    private static byte[] toDecodedBase64ByteArray(byte[] base64EncodedByteArray) {
      return DatatypeConverter.parseBase64Binary(
          new String(base64EncodedByteArray, Charset.forName("UTF-8")));
    }
    

    主要问题是您拥有证书而不是公钥。证书包含公钥,但无法使用X509EncodedKeySpec(...) 加载,这就是为什么必须改用CertificateFactory

    (顺便说一句,here 是一篇关于 openssl 和 Java 密码学用法的精彩文章/教程。我的部分信息来自那里。)

    【讨论】:

    • 我的密钥存储在 base64 中,但是当我尝试执行以下代码对其进行解密时:PKCS8Key pkcs8 = new PKCS8Key( llave, password.toCharArray()); 我收到以下错误:org.apache.commons.ssl.ProbablyNotPKCS8Exception: asn1 parse failure: java.lang.ClassCastException: org.apache.commons.ssl.asn1.DERApplicationSpecific cannot be cast to org.apache.commons.ssl.asn1.DERSequence。我应该怎么做?
    • @user1084509 我链接的页面在顶部附近说 InputStream 可以是 DER(原始 ASN.1)或 PEM(base64)。 你说过你有一个 DER 格式的密钥。如果你有这个以 Base64 编码的原始 DER 密钥,那么首先你必须对其进行解码(可能)。如果您使用的是 JDK 6,那么您可以使用 parseBase64Binary(...)。我会用一个例子来更新我的答案。
    • 哇!非常感谢!这确实适用于我的私钥。现在问题出在公钥上,因为我使用 parseBase64Binary (...) 函数对其进行解码,但是对于同样是 DER 编码但具有 X509 格式的公钥是否存在 PKCS8Key pkcs8 = new PKCS8Key( llave2, password.toCharArray()); llave2 = pkcs8.getDecryptedBytes(); 等价?
    • @user1084509 现在问题出在公钥上,因为我使用 parseBase64Binary (...) 函数对其进行解码 它是否像您的私钥一样以 Base64 编码?如果是,您应该对其进行解码;否则不是。 PKCS8Key 类仅用于因为如果您的私钥已加密(与 encoded 不同)PKCS8EncodedKeySpec 就不会吃掉它。对于证书,您可以尝试将您的密钥(作为字节数组)传递给 X509EncodedKeySpec 的构造函数。而且我不明白您问题的 is there a equivalence of [...] 部分。更新您的问题并提供解释。
    • 非常感谢您的帮助,因为这让我发疯了。我已经更新了我的问题,你可以看看。
    猜你喜欢
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    • 1970-01-01
    • 2015-12-17
    • 2020-12-01
    • 1970-01-01
    • 2018-07-10
    • 1970-01-01
    相关资源
    最近更新 更多