从Base64 DER - Java构建PublicKey

时间:2015-06-30 08:14:02

标签: java encryption base64 rsa der

我在将base64编码的DER证书传递给Java应用程序时遇到问题,因为它从中提取公钥。我可以在Objective-c& ruby,但我正在努力解决Java中的错误。

我在Ruby(简化)中创建了以下DER base64密钥:

key = OpenSSL::PKey::RSA.new(2048)

public_key = key.public_key

subject = "/C=BE/O=Test/OU=Test/CN=Test"

cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
cert.not_before = Time.now
cert.not_after = Time.now + 365 * 24 * 60 * 60
cert.public_key = public_key
cert.serial = 0x0
cert.version = 2

ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = cert
cert.extensions = [
  ef.create_extension("basicConstraints","CA:TRUE", true),
  ef.create_extension("subjectKeyIdentifier", "hash"),
  # ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
]
cert.add_extension ef.create_extension("authorityKeyIdentifier",
                                       "keyid:always,issuer:always")

cert.sign key, OpenSSL::Digest::SHA1.new


der => Base64.encode64(cert.to_der)

以下是输出示例:

MIIDhzCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQUFADA6MQswCQYDVQQGEwJC\nRTENMAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVzdDENMAsGA1UEAwwEVGVz\ndDAeFw0xNTA1MjExNDUxNTZaFw0xNjA1MjAxNDUxNTZaMDoxCzAJBgNVBAYT\nAkJFMQ0wCwYDVQQKDARUZXN0MQ0wCwYDVQQLDARUZXN0MQ0wCwYDVQQDDARU\nZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvbC3phpnj/Vg\neIAmWXD3TkGi91kPFBvrrD/LLa4kv83eOuY139vUn/xjZlM9maE36Yix6Ix1\ncF3cGCUl1VZApJYTef404jL13xLg1i9+96/tU91niZMlRkFL0mZWV2XhEzNH\nnA+lRiJZlGdXNwYUKXb9qVRuS2taSAyMwH/SDPu1s17SGVLY1o+7trAQfK7w\ny5w8fyTr+tCdcplb5F/m6R5FXc4eJwD6m9MYnenlmkoW5uM5B1lbQBVz2by3\nVGCMUmLwpC15pi13/fIJ8WzD1SQcYJgIQTauWVRYxSuc+Cg0VvoyMZNqkqOW\n7iKmZXGqwNQKxhLtcc1KNJky7OHLkQIDAQABo4GXMIGUMA8GA1UdEwEB/wQF\nMAMBAf8wHQYDVR0OBBYEFNWHBjgj9rsOBm6Z9+me3I/E1ZrUMGIGA1UdIwRb\nMFmAFNWHBjgj9rsOBm6Z9+me3I/E1ZrUoT6kPDA6MQswCQYDVQQGEwJCRTEN\nMAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVzdDENMAsGA1UEAwwEVGVzdIIB\nADANBgkqhkiG9w0BAQUFAAOCAQEAbKYmQxaQaGT57Qq8xYzIzODGqjbek3qC\n8kSjUto9H/5o8OCKqDFJfgaYAS9mgEjjazqmMahoDeLvzRkKHkpXLvdjjjv7\nnnMZGIw7I4yOKvtzGDz2eimonlWPePypTwr0NFnnUByQb9nkrOOrpcSKBn7a\nwvIT7b82ISOoMz1+hlyo8dyiZri82J6pKXTP91LcfpSRiC/1W1sXnIL5DSJi\nDtXGMVtDfy9rRgPJhmOPu4xqInl/o+t2A1OXLhA4aDnxP/gbssVau9Do3uIa\nOlyo9eGpatIvkxCMzC4SgBavBy6Gsk2p4KAuWon9TtDzO5vklEI8QKk1tiyJ\nYZBCeK3HwQ==\n

如果我要在Ruby中获取公钥,我会这样做:

der = Base64.decode64(data["certs"]["device_key"])
x509_cert = OpenSSL::X509::Certificate.new  der
public_rsa_2048_key = x509_cert.public_key

Java中的相同内容有点冗长,但(归功于SO)这就是我所拥有的:

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.io.File;
import java.security.cert.CertificateFactory;
import java.io.ByteArrayInputStream;
import java.security.cert.Certificate;
import java.security.PublicKey;
import java.io.FileInputStream;
import java.nio.charset.Charset;
import java.nio.channels.FileChannel;
import java.io.ByteArrayOutputStream;
import javax.xml.bind.DatatypeConverter;
import java.nio.channels.Channels;
import java.io.Console;



public class EncryptionTest{

  public static void main(String[] args) throws IOException, GeneralSecurityException {
    Console console = System.console();

    console.writer().println("Loading base64 key from file");

    // get a handle on the base64 encoded key and certificate
    // File privateKeyFile = new File("private.der.b64");
    File publicKeyFile = new File("public.der.b64");

    console.writer().println("Converting to byteArray");

    // pull them into arrays
    // byte[] privateKeyBytes = toByteArray(privateKeyFile);
    byte[] publicKeyBytes = toByteArray(publicKeyFile);

    console.writer().println("decoding base64 bytes");

    // 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);

    console.writer().println("Building Cert");

    // get the public key
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
    Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(publicKeyBytes));

    console.writer().println("Extracting Public Key");  

    PublicKey publicKey = certificate.getPublicKey();

    console.writer().println("Key:" + publicKey.toString());
  }

  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")));
  }
}

但是,当我运行此操作时,我收到以下错误:

Exception in thread "main" java.security.cert.CertificateException: Unable to initialize, java.io.IOException: DerInputStream.getLength(): lengthTag=105, too big.
    at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:199)
    at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:98)
    at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
    at EncryptionTest.main(EncryptionTest.java:50)
Caused by: java.io.IOException: DerInputStream.getLength(): lengthTag=105, too big.
    at sun.security.util.DerInputStream.getLength(DerInputStream.java:561)
    at sun.security.util.DerValue.<init>(DerValue.java:252)
    at sun.security.util.DerInputStream.getDerValue(DerInputStream.java:417)
    at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1761)
    at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:196)

关于如何在Java中解决这个问题的任何指针都会很棒!感谢

1 个答案:

答案 0 :(得分:1)

您需要用\n替换示例输出中的linefeed character。那么你的代码不会失败。

输出(长行截断)

Loading base64 key from file
Converting to byteArray
decoding base64 bytes
Building Cert
Extracting Public Key
Key:Sun RSA public key, 2048 bits
  modulus: 239461822256650353755672 ...
  public exponent: 65537

编辑替换\n的一种可能解决方案是将其替换为两个换行符(仅因为不更改数组中的字节数)。

修改您的方法toByteArray,如下所示。

private static byte[] toByteArray(File file) throws IOException {
    byte[] allBytes = Files.readAllBytes(file.toPath());
    for (int i = 0; i < allBytes.length; i++) {
        if (allBytes[i] == '\\') {
            allBytes[i++] = 10;
            allBytes[i++] = 10;
        }
    }
    return allBytes;
}