Создать PrivateKey и PublicKey из кодировки String base64 в формате DER - PullRequest
13 голосов
/ 10 декабря 2011

У меня есть свой закрытый и открытый ключи в строке в base64, которая была закодирована с использованием ANS1 DER.Я попытался создать экземпляр java PrivateKey и PublicKey:

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. Потому что сертификат также зашифрован.Есть предложения?

1 Ответ

33 голосов
/ 10 декабря 2011

Чтобы проверить ваш сценарий, я создал закрытый ключ RSA с openssl.

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 как к форматам , поэтому, насколько я понимаю, происходит следующее:

  • pkcs8openssl сообщает, что я хочу работать с закрытыми ключами в формате PKCS # 8.
  • -topk8 сообщает, что секретный ключ, который я собираюсь указать с помощью -in, равен , а не в PKCS # 8 (иначе это будет предполагаться).
  • -inform и -in указывают, что я хочу преобразовать закрытый ключ (PEM) в PKCS # 8 (без -topk8 itпопробую преобразовать ключ уже в формате PKCS # 8 в стандартный формат ключа).
  • -outform и -out сообщают, что я хочу отформатированный ключ DER в качестве вывода.
  • -nocrypt говорит, что я не хочу шифровать ключ.

Затем, с моим ключом RSA (в стандартном формате), я создал сертификат.

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

Сертификатns открытый ключ, соответствующий моему личному ключу.

После всего этого я закодировал и личный ключ, и сертификат с 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. Оттуда я получил частично информацию.)

...