使用DER格式从String base64编码创建PrivateKey和PublicKey



我有我的私钥和公钥在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行中对私钥所做的事情。因为证书也是加密的。有什么建议吗?

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

openssl genrsa -out private.pem 1024

然后我把这个密钥转换成pkcs# 8der格式。

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中是而不是(否则它会假设它是)。
  • -inform-in指定我要将(PEM)私钥转换为pkcs# 8(没有-topk8,它将尝试将pkcs# 8格式的密钥转换为标准密钥格式)。
  • -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来代替。

(顺便说一下,这里有一篇关于openssl和Java加密用法的优秀文章/教程。)

最新更新