Java public key to pem

Using openssl and java for RSA keys

If you want to use public key encryption, you’ll need public and private keys in some format. OpenSSL and many other tools can generate such key pairs as well as java. However, if it comes to interoperability between these tools, you’ll need to be a bit careful. This post shows, how to generate a key pair with openssl, store it in files and load these key pairs in Java for usage.

Part 1: Generate a fresh key pair with openssl

You start with generating a private key using the genrsa tool from OpenSSL:

openssl genrsa -out privatekey.pem 2048 

This creates a new RSA private key with 2048 bits length. The key is stored in the file privatekey.pem and it is in the “PEM” format. The PEM format is essentially a base64-encoded variant of a DER-encoded structure. You can look at the file, it should start with an “BEGIN RSA PRIVATE KEY” header and end with “END RSA PRIVATE KEY” footer:

head -2 privatekey.pem; tail -1 privatekey.pem -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAth6P/MXUGL1y69Ao9THV16taSeUWnM4FQpmHP0yMDS3hB4V0 -----END RSA PRIVATE KEY----- 

This file is now all we need to get started. Although this seems just to be the private key and the public key seems to be missing — it is not: This private key format contains all the information to reconstruct the public key data.

Part 2: Extract the public key

The second tool from OpenSSL we’ll use is rsa . This allows to do some conversions of the key files.

openssl rsa -in privatekey.pem -out publickey.pem -pubout 

If we look at the generate file publickey.pem , we see, that is also in the PEM format. The header and footer lines are “BEGIN PUBLIC KEY” and “END PUBLIC KEY” respectively:

head -2 publickey.pem; tail -1 publickey.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAth6P/MXUGL1y69Ao9THV -----END PUBLIC KEY----- 

Now we have the plain key files available. You could distribute the public key file to allow the other party to encrypt some data while keeping the private key save. Please note, that the private key file is not encrypted and must be secured in some way (like file permissions, etc.).

Читайте также:  Проверка ключа массива python

Part 3: Understanding the key files structure

Java itself cannot directly load the PEM files generated in the above steps. However, the PEM files are actually just the “DER” format base 64 encoded with the additional headers and footers. But what is the “DER” format? The man page explains a bit:

-inform DER|NET|PEM This specifies the input format. The DER option uses an ASN1 DER encoded form compatible with the PKCS#1 RSAPrivateKey or SubjectPublicKeyInfo format. The PEM form is the default format: it consists of the DER format base64 encoded with additional header and footer lines. On input PKCS#8 format private keys are also accepted. The NET form is a format is described in the NOTES section. 

So there is the standard PKCS#1 which defines the structures RSAPrivateKey and SubjectPublicKeyInfo . The standard has been published also as RFC 3447. The recommendation for interchanging private keys is described in appendix A.1.2:

You can see two things: The structure is basically an list of numbers. And the private key structure contains the modulus — that’s the reason why you can extract the public key from this private key file.

The public key structure SubjectPublicKeyInfo is described in appenx A.1.1:

You can display this info in “human readable” format using OpenSSL, too:

openssl rsa -in privatekey.pem -text Private-Key: (2048 bit) modulus: 00:b6:1e:8f:fc:c5:d4:18:bd:72:eb:d0:28:f5:31: . publicExponent: 65537 (0x10001) privateExponent: 00:a9:f4:cb:9a:b1:63:c5:d2:c6:b4:9a:86:1e:8c: . It will display all the fields. The same is possible with the public key: openssl rsa -in publickey.pem -text -pubin Public-Key: (2048 bit) Modulus: 00:b6:1e:8f:fc:c5:d4:18:bd:72:eb:d0:28:f5:31: . 

Part 4: Converting the key files for usage in Java (Public Key)

Plain Java is able to understand the public key format. However, it can’t read the PEM file directly, but it can understand the DER encoding. The solution is, to decode the file first using Base64 and then let it parse by Java. Here’s a snippet that does this:

public static PublicKey loadPublicKey() throws Exception < String publicKeyPEM = FileUtils.readFileToString(new File("publickey.pem"), StandardCharsets.UTF_8); // strip of header, footer, newlines, whitespaces publicKeyPEM = publicKeyPEM .replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); // decode to get the binary DER representation byte[] publicKeyDER = Base64.getDecoder().decode(publicKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyDER)); return publicKey; >

It uses X509EncodedKeySpec to load the public key, which is just the recommended SubjectPublicKeyInfo implementation.

Читайте также:  Html style font position

Note: I used Apache’s commons-io library for FileUtils . Everything else is include in the standard Java8 JDK.

Part 5: Convert the private key for for usage in Java

Unfortunately we can’t use the exact same trick for the private key. Java has an encoded key spec for the private key: PKCS8EncodedKeySpec — however, it implements “PKCS#8” rather than “PKCS#1” that we used. Luckily, OpenSSL contains also a converter for this format:

openssl pkcs8 -in privatekey.pem -topk8 -nocrypt -out privatekey-pkcs8.pem 

If you inspect the generated file, you’ll see again the PEM format, but now with the header “BEGIN PRIVATE KEY”:

head -2 privatekey-pkcs8.pem; tail -1 privatekey-pkcs8.pem -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2Ho/8xdQYvXLr -----END PRIVATE KEY----- 

Please note, that this private key file is also not encrypted ( nocrypt ) and must be kept safe.

This format is described in RFC 5208 and the structure in section 5:

It is again DER encoded and it is actually just wrapping the RSAPrivateKey structure from above in the privateKey field. However, this format allows for encrypting the private key with a password (which we don’t use in this example).

Now we can load the private key in Java, too:

public static PrivateKey loadPrivateKey() throws Exception < String privateKeyPEM = FileUtils.readFileToString(new File("privatekey-pkcs8.pem"), StandardCharsets.UTF_8); // strip of header, footer, newlines, whitespaces privateKeyPEM = privateKeyPEM .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); // decode to get the binary DER representation byte[] privateKeyDER = Base64.getDecoder().decode(privateKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyDER)); return privateKey; >

Part 6: Encrypting and Decrypting in Java using RSA

Now we can use Java to encrypt and decrypt like this:

public static void main(String[] args) throws Exception < Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); String clearText = "Sample plain text"; PublicKey publicKey = loadPublicKey(); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] encrypted = cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8)); PrivateKey privateKey = loadPrivateKey(); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decrypted = cipher.doFinal(encrypted); System.out.println("ClearText: " + clearText); System.out.println("Decrypted: " + new String(decrypted, StandardCharsets.UTF_8)); System.out.println("ClearText length: " + clearText.getBytes(StandardCharsets.UTF_8).length); System.out.println("Encrypted length: " + encrypted.length); System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted)); >

The output looks like this:

ClearText: Sample plain text Decrypted: Sample plain text ClearText length: 17 Encrypted length: 256 Encrypted: riHHycTvKaDtX3SkeoZbFCW3KW3vxEIsF3wVQqOKuwAbTtWFyP6yN5essem+jTx16Ggdp6/rzS9r9Wy5O6P8JuOQAKi. 

You can see that the clear text has been bloated up to 256 bytes. This is becaue I generated a RSA key with 2048 bits length, which are 256 bytes. The RSA cipher encrypts in blocks and uses padding in the blocks. I used “PKCS1Padding” which uses 11 bytes for padding, which means, you can at most encrypt 256-11=245 bytes of plain data in one block. If you have bigger data to encrypt, you’ll need to chain these blocks. There are different ways to chain blocks: Electronic Codebook (ECB), Cipher Block Chaining (CBC). See Block cipher mode of operation. You could also consider using a hybrid method, which means, you’ll exchange a symmetric key for AES via RSA first and then use this AES key for the bigger data you want to exchange.

Читайте также:  nth-child

Comments

andreas_dangel

Have you searched for an answer on https://stackoverflow.com/s… ?
What does your question has to do with FritzBox?

andreas_dangel

Hm, not sure why disqus is displaying threads on wrong pages. not what I would expect.

> Very strange thing is that if I’m trying to encrypt CipherData.enc, the result I
> get is not equal to CipherData.txt

Encrypting the same text again with the same password typically results in a different cryptext for security reason. This behavior depends on the padding, which pads up the plaintext to a certain length (so that the encryption algorithm can encrypt it) and the padding that is used is usually random. See https://en.wikipedia.org/wi. .

> Unfortunately: bad magic number

You can start openssl with the verbose flag (`-v`). Maybe that sheds some light.

In https://unix.stackexchange. they suggest, to specify the hash algorithm. However, using wrong hash algorithm result for me in a «bad decrypt» error message.

Leave a comment

Your email address will not be published. Required fields are marked * . All comments are held for moderation to avoid spam and abuse.

Источник

Оцените статью