Cara Membaca File PEM untuk Mendapatkan Kunci Publik dan Pribadi

Java Top

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya

1. Ikhtisar

Dalam kriptografi kunci publik (juga dikenal sebagai kriptografi asimetris), mekanisme enkripsi bergantung pada dua kunci terkait, kunci publik dan kunci privat. Kunci publik digunakan untuk mengenkripsi pesan sementara hanya pemilik kunci privat yang dapat mendekripsi pesan.

Dalam tutorial ini, kita akan melihat cara membaca kunci publik dan pribadi dari file PEM.

Pertama, kita akan mempelajari beberapa konsep penting seputar kriptografi kunci publik. Kemudian, kita akan belajar cara membaca file PEM menggunakan Java murni.

Terakhir, kita akan menjelajahi pustaka BouncyCastle sebagai pendekatan alternatif.

2. Konsep

Sebelum kita mulai, mari kita pahami beberapa konsep utama.

X.509 adalah standar yang menentukan format sertifikat kunci publik. Jadi, format ini menjelaskan kunci publik di antara informasi lainnya.

DER adalah format pengkodean paling populer untuk menyimpan data seperti sertifikat X.509, kunci pribadi PKCS8 dalam file. Ini adalah pengkodean biner dan konten yang dihasilkan tidak dapat dilihat dengan editor teks.

PKCS8 adalah sintaks standar untuk menyimpan informasi kunci privat. Kunci pribadi dapat dienkripsi secara opsional menggunakan algoritma simetris.

Kunci pribadi RSA tidak hanya dapat ditangani oleh standar ini, tetapi juga algoritme lainnya. Kunci pribadi PKCS8 biasanya dipertukarkan melalui format pengkodean PEM.

PEM adalah mekanisme pengkodean basis-64 dari sertifikat DER. PEM juga dapat menyandikan jenis data lain seperti kunci publik / pribadi dan permintaan sertifikat.

File PEM juga berisi header dan footer yang menjelaskan jenis data yang dikodekan:

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

3. Menggunakan Pure Java

3.1. Membaca Data PEM Dari File

Mari kita mulai dengan membaca file PEM dan menyimpan isinya ke dalam string:

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

3.2. Dapatkan Kunci Publik Dari PEM String

Kita akan membuat metode utilitas yang mendapatkan kunci publik dari string yang dikodekan 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-----

Misalkan kita menerima File sebagai parameter:

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); }

Seperti yang bisa kita lihat, pertama-tama kita perlu menghapus header, footer, dan baris baru juga. Kemudian, kita perlu mendekode string yang dikodekan Base64 ke dalam format biner yang sesuai.

Selanjutnya, kita perlu memuat hasilnya ke dalam kelas spesifikasi kunci yang mampu menangani material kunci publik. Dalam kasus kami, kami akan menggunakan kelas X509EncodedKeySpec .

Akhirnya, kita bisa menghasilkan objek kunci publik dari spesifikasi menggunakan kelas KeyFactory .

3.3. Dapatkan Kunci Pribadi Dari PEM String

Sekarang kita tahu cara membaca kunci publik, algoritme untuk membaca kunci pribadi sangat mirip.

Kami akan menggunakan kunci pribadi yang dikodekan PEM dalam format PKCS8. Mari kita lihat seperti apa tampilan header dan footer:

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

Seperti yang kita pelajari sebelumnya, kita membutuhkan kelas yang mampu menangani materi kunci PKCS8. Kelas PKCS8EncodedKeySpec mengisi peran itu.

Jadi, mari kita lihat algoritmanya:

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. Menggunakan Perpustakaan BouncyCastle

4.1. Baca Kunci Publik

Kita akan menjelajahi pustaka BouncyCastle dan melihat bagaimana itu dapat digunakan sebagai alternatif untuk implementasi Java murni.

Mari kita dapatkan kunci publiknya:

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); } }

Ada beberapa kelas penting yang perlu kita waspadai saat menggunakan BouncyCastle:

  • PemReader - menggunakan Reader sebagai parameter dan mem-parsing isinya. Ini menghapus header yang tidak perlu dan menerjemahkan data Base64 PEM yang mendasarinya ke dalam format biner.
  • PemObject - menyimpan hasil yang dihasilkan oleh PemReader .

Selain itu, mari kita lihat pendekatan lain yang membungkus kelas Java ( X509EncodedKeySpec, KeyFactory ) ke dalam kelas BouncyCastle sendiri ( 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. Baca Kunci Pribadi

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.

Terakhir, kami menjelajahi pustaka BouncyCastle dan mengetahui bahwa ini adalah alternatif yang baik karena memberikan beberapa keuntungan dibandingkan dengan implementasi Java murni.

Kode sumber lengkap untuk pendekatan Java dan BouncyCastle tersedia di GitHub.

Jawa bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya