Java: OpenSSL RSA PEM Public-Key in Java verwenden

Oft liegen RSA-Schlüssel im PEM-Format vor. Von Haus aus kann Java nur eingeschränkt mit PEM-Dateien umgehen. Es ist deshalb oft etwas umständlich, einen Schlüssel zu verwenden.

Falls es sich um einen öffentlichen Schlüssel handelt, der nicht regelmässig neu eingelesen werden muss, gibt es (neben diversen anderen Verfahren) eine recht simple Möglichkeit, ihn Java-tauglich aufzubereiten:  Relevant ist für die Verwendung lediglich der Modulus und der Exponent. Diese beiden Angaben sind via OpenSSL sehr einfach zu erhalten.

Im folgenden Beispiel wollen wir einen Text mit dem folgenden öffentlichen Schlüssel aus einer Datei pubkey.pem verschlüsseln:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAospZT3nvYk1lsYBIv/n+
JQHWcMWxsHefh9A2O+BuyDpkISfgb73W/+VWcU7MRhWdI2663e9eZiqblFVHhyHR
3fH0y6QLxrDrIf5RvrVHuDcIHRaV/nUYM3GoWLM3FI5chdWnWHTBr95V9XQ8RKi7
ZJKg+CsLPWQWwn5C4GYr1zI9dUrc4L32l7+ykErf/v7Yv9XETbhOfHGixseIjEnk
h5/bWCtWsMpWeRH3zZ4AptWdQVFGnkjG26NzfYEvXYRFiOlAPxb0pJF6cQ8990UD
GeyNNdMNeYcAv5nxdd7wn1AHM7ZFtUhXyBClVoiN8WWrWtYIvo0+YyfOqZHDxtRY
OQIDAQAB
-----END PUBLIC KEY-----

 

Modulus und Exponent lassen sich mit OpenSSL recht einfach extrahieren:

$ openssl rsa -pubin -text -noout -inform PEM < pubkey.pem 
Public-Key: (2048 bit)
Modulus:
    00:a2:ca:59:4f:79:ef:62:4d:65:b1:80:48:bf:f9:
    fe:25:01:d6:70:c5:b1:b0:77:9f:87:d0:36:3b:e0:
    6e:c8:3a:64:21:27:e0:6f:bd:d6:ff:e5:56:71:4e:
    cc:46:15:9d:23:6e:ba:dd:ef:5e:66:2a:9b:94:55:
    47:87:21:d1:dd:f1:f4:cb:a4:0b:c6:b0:eb:21:fe:
    51:be:b5:47:b8:37:08:1d:16:95:fe:75:18:33:71:
    a8:58:b3:37:14:8e:5c:85:d5:a7:58:74:c1:af:de:
    55:f5:74:3c:44:a8:bb:64:92:a0:f8:2b:0b:3d:64:
    16:c2:7e:42:e0:66:2b:d7:32:3d:75:4a:dc:e0:bd:
    f6:97:bf:b2:90:4a:df:fe:fe:d8:bf:d5:c4:4d:b8:
    4e:7c:71:a2:c6:c7:88:8c:49:e4:87:9f:db:58:2b:
    56:b0:ca:56:79:11:f7:cd:9e:00:a6:d5:9d:41:51:
    46:9e:48:c6:db:a3:73:7d:81:2f:5d:84:45:88:e9:
    40:3f:16:f4:a4:91:7a:71:0f:3d:f7:45:03:19:ec:
    8d:35:d3:0d:79:87:00:bf:99:f1:75:de:f0:9f:50:
    07:33:b6:45:b5:48:57:c8:10:a5:56:88:8d:f1:65:
    ab:5a:d6:08:be:8d:3e:63:27:ce:a9:91:c3:c6:d4:
    58:39
Exponent: 65537 (0x10001)

 

Um den Modulus in einem Format zu bekommen, der sich direct per Copy&Paste in Java verwenden lässt, nehmen wir diese Modifikationen vor:

$ openssl rsa -pubin -text -noout -inform PEM < pubkey.pem \
| grep "^  " | tr -s '\n' ' ' |sed -e 's/://g' -e 's/ //g'
00a2ca594f79ef624d65b18048bff9fe2501d670c5b1b0779f87d03[...]5839

 

In Java können wir nun den Exponenten und den Modulus direkt dazu verwenden, um ein Public-Key-Objekt zu erhalten:

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;

[...]
// get modulus and exponent via openssl rsa -in etc/pubkey.pem -pubin -noout -text
RSAPublicKeySpec spec = new RSAPublicKeySpec(
  new BigInteger( "00a2ca594f7[...]39", 16 ),
  new BigInteger( "10001", 16 )
);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = kf.generatePublic(spec);

 

Das so erhaltene Public-Key-Objekt können wir nun z.B. verwenden, um Text zu verschlüsseln:

Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);

String text = "Das ist unser Klartext";
byte[] encrypted = cipher.doFinal(text.getBytes());
System.out.println( "verschluesselter Text: " + Base64.getEncoder().encodeToString(encrypted) );