Как да чета PEM файл, за да получа публични и частни ключове

Java Top

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА

1. Общ преглед

В криптографията с публичен ключ (известна също като асиметрична криптография) механизмът за криптиране разчита на два свързани ключа, публичен ключ и частен ключ. Публичният ключ се използва за шифроване на съобщението, докато само собственикът на частния ключ може да дешифрира съобщението.

В този урок ще видим как да четем публични и частни ключове от PEM файл.

Първо ще проучим някои важни концепции около криптографията с публичен ключ. След това ще научим как да четем PEM файлове, използвайки чиста Java.

И накрая, ще разгледаме библиотеката BouncyCastle като алтернативен подход.

2. Понятия

Преди да започнем, нека разберем някои ключови понятия.

X.509 е стандарт, определящ формата на сертификатите с публичен ключ. И така, този формат описва публичен ключ наред с друга информация.

DER е най-популярният формат за кодиране за съхраняване на данни като X.509 сертификати, PKCS8 частни ключове във файлове. Това е двоично кодиране и полученото съдържание не може да се гледа с текстов редактор.

PKCS8 е стандартен синтаксис за съхраняване на информация от частния ключ. Частният ключ може да бъде криптиран по избор с помощта на симетричен алгоритъм.

Този стандарт не само може да обработва RSA частни ключове, но и други алгоритми. Частните ключове на PKCS8 обикновено се обменят чрез PEM кодиращия формат.

PEM е механизъм за кодиране base-64 на DER сертификат. PEM може да кодира и други видове данни като публични / частни ключове и заявки за сертификати.

PEM файлът съдържа също горен колонтитул, описващ типа на кодираните данни:

-----BEGIN PUBLIC KEY----- ...Base64 encoding of the DER encoded certificate... -----END PUBLIC KEY-----

3. Използване на Pure Java

3.1. Четете PEM данни от файл

Нека започнем с четене на PEM файла и съхраняване на съдържанието му в низ:

String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());

3.2. Вземете публичен ключ от PEM String

Ще изградим полезен метод, който получава публичния ключ от кодирания в PEM низ:

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjtGIk8SxD+OEiBpP2/T JUAF0upwuKGMk6wH8Rwov88VvzJrVm2NCticTk5FUg+UG5r8JArrV4tJPRHQyvqK wF4NiksuvOjv3HyIf4oaOhZjT8hDne1Bfv+cFqZJ61Gk0MjANh/T5q9vxER/7TdU NHKpoRV+NVlKN5bEU/NQ5FQjVXicfswxh6Y6fl2PIFqT2CfjD+FkBPU1iT9qyJYH A38IRvwNtcitFgCeZwdGPoxiPPh1WHY8VxpUVBv/2JsUtrB/rAIbGqZoxAIWvijJ Pe9o1TY3VlOzk9ASZ1AeatvOir+iDVJ5OpKmLnzc46QgGPUsjIyo6Sje9dxpGtoG QQIDAQAB -----END PUBLIC KEY-----

Да предположим, че получаваме файл като параметър:

public static RSAPublicKey readPublicKey(File file) throws Exception { String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset()); String publicKeyPEM = key .replace("-----BEGIN PUBLIC KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PUBLIC KEY-----", ""); byte[] encoded = Base64.decodeBase64(publicKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded); return (RSAPublicKey) keyFactory.generatePublic(keySpec); }

Както виждаме, първо трябва да премахнем горния, долния и долния колонтитул, както и новите редове. След това трябва да декодираме кодирания низ Base64 в съответния му двоичен формат.

След това трябва да заредим резултата в клас за спецификация на ключове, способен да обработва материал с публичен ключ. В нашия случай ще използваме класа X509EncodedKeySpec .

И накрая, можем да генерираме обект с публичен ключ от спецификацията, използвайки класа KeyFactory .

3.3. Вземете частен ключ от PEM String

Сега, когато знаем как да четем публичен ключ, алгоритъмът за четене на частен ключ е много подобен.

Ще използваме PEM кодиран частен ключ във формат PKCS8. Нека да видим как изглеждат горният и долният колонтитул:

-----BEGIN PRIVATE KEY----- ...Base64 encoded key... -----END PRIVATE KEY-----

Както научихме по-рано, имаме нужда от клас, който да може да обработва ключови материали за PKCS8. Класът PKCS8EncodedKeySpec изпълнява тази роля.

И така, нека видим алгоритъма:

public RSAPrivateKey readPrivateKey(File file) throws Exception { String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset()); String privateKeyPEM = key .replace("-----BEGIN PRIVATE KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PRIVATE KEY-----", ""); byte[] encoded = Base64.decodeBase64(privateKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); }

4. Използване на библиотека BouncyCastle

4.1. Прочетете публичния ключ

Ще проучим библиотеката на BouncyCastle и ще видим как тя може да се използва като алтернатива на чистото изпълнение на Java.

Нека вземем публичния ключ:

public RSAPublicKey readPublicKey(File file) throws Exception { KeyFactory factory = KeyFactory.getInstance("RSA"); try (FileReader keyReader = new FileReader(file); PemReader pemReader = new PemReader(keyReader)) { PemObject pemObject = pemReader.readPemObject(); byte[] content = pemObject.getContent(); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content); return (RSAPublicKey) factory.generatePublic(pubKeySpec); } }

Има няколко важни класа, с които трябва да сме наясно, когато използваме BouncyCastle:

  • PemReader - взема Reader като параметър и анализира съдържанието му. Той премахва ненужните заглавки и декодира основните PEM данни на Base64 в двоичен формат.
  • PemObject - съхранява резултата, генериран от PemReader .

Освен това, нека видим друг подход, който обгръща класовете на Java ( X509EncodedKeySpec, KeyFactory ) в собствения клас на BouncyCastle ( JcaPEMKeyConverter ):

public RSAPublicKey readPublicKeySecondApproach(File file) throws IOException { try (FileReader keyReader = new FileReader(file)) { PEMParser pemParser = new PEMParser(keyReader); JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(pemParser.readObject()); return (RSAPublicKey) converter.getPublicKey(publicKeyInfo); } }

4.2. Прочетете частния ключ

We're going to see two examples that are very similar to the ones showed above.

In the first example, we just need to replace the X509EncodedKeySpec class with the PKCS8EncodedKeySpec class and return an RSAPrivateKey object instead of an RSAPublicKey:

public RSAPrivateKey readPrivateKey(File file) throws Exception { KeyFactory factory = KeyFactory.getInstance("RSA"); try (FileReader keyReader = new FileReader(file); PemReader pemReader = new PemReader(keyReader)) { PemObject pemObject = pemReader.readPemObject(); byte[] content = pemObject.getContent(); PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content); return (RSAPrivateKey) factory.generatePrivate(privKeySpec); } }

Now, let's rework a bit the second approach from the previous section in order to read a private key:

public RSAPrivateKey readPrivateKeySecondApproach(File file) throws IOException { try (FileReader keyReader = new FileReader(file)) { PEMParser pemParser = new PEMParser(keyReader); JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(pemParser.readObject()); return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo); } }

As we can see, we just replaced SubjectPublicKeyInfo with PrivateKeyInfo and RSAPublicKey with RSAPrivateKey.

4.3. Advantages

There are a couple of advantages provided by the BouncyCastle library.

One advantage is that we don’t need to manually skip or remove the header and the footer. Another one is that we’re not responsible for the Base64 decoding either. Therefore, we can write less error-prone code with BouncyCastle.

Moreover, the BouncyCastle library supports the PKCS1 format as well. Despite the fact that PKCS1 is also a popular format used to store cryptographic keys (only RSA keys), Java doesn't support it on its own.

5. Conclusion

In this article, we learned how to read public and private keys from PEM files.

First, we studied a few key concepts around public-key cryptography. Then, we saw how to read public and private keys using pure Java.

И накрая, изследвахме библиотеката BouncyCastle и научихме, че това е добра алтернатива, тъй като предоставя няколко предимства в сравнение с чистото изпълнение на Java.

Пълният изходен код за подходите за Java и BouncyCastle е достъпен в GitHub.

Дъно на Java

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА