From ff310e08e608f26205043f3db3db21747ab2764d Mon Sep 17 00:00:00 2001 From: Catalin Burcea Date: Sun, 12 Jul 2020 21:03:46 +0300 Subject: [PATCH] BAEL-4219 - How to read .pem file to get private and public key (#9676) --- .../baeldung/pem/JavaSecurityPemUtils.java | 48 +++++++++++++ .../main/resources/pem/private-key-pkcs8.pem | 28 ++++++++ .../src/main/resources/pem/public-key.pem | 9 +++ .../pem/JavaSecurityPemUtilsUnitTest.java | 33 +++++++++ .../baeldung/pem/BouncyCastlePemUtils.java | 71 +++++++++++++++++++ .../main/resources/pem/private-key-pkcs8.pem | 28 ++++++++ .../src/main/resources/pem/public-key.pem | 9 +++ .../pem/BouncyCastlePemUtilsUnitTest.java | 41 +++++++++++ 8 files changed, 267 insertions(+) create mode 100644 core-java-modules/core-java-security-2/src/main/java/com/baeldung/pem/JavaSecurityPemUtils.java create mode 100644 core-java-modules/core-java-security-2/src/main/resources/pem/private-key-pkcs8.pem create mode 100644 core-java-modules/core-java-security-2/src/main/resources/pem/public-key.pem create mode 100644 core-java-modules/core-java-security-2/src/test/java/com/baeldung/pem/JavaSecurityPemUtilsUnitTest.java create mode 100644 libraries-security/src/main/java/com/baeldung/pem/BouncyCastlePemUtils.java create mode 100644 libraries-security/src/main/resources/pem/private-key-pkcs8.pem create mode 100644 libraries-security/src/main/resources/pem/public-key.pem create mode 100644 libraries-security/src/test/java/com/baeldung/pem/BouncyCastlePemUtilsUnitTest.java diff --git a/core-java-modules/core-java-security-2/src/main/java/com/baeldung/pem/JavaSecurityPemUtils.java b/core-java-modules/core-java-security-2/src/main/java/com/baeldung/pem/JavaSecurityPemUtils.java new file mode 100644 index 0000000000..2697072e27 --- /dev/null +++ b/core-java-modules/core-java-security-2/src/main/java/com/baeldung/pem/JavaSecurityPemUtils.java @@ -0,0 +1,48 @@ +package com.baeldung.pem; + +import org.apache.commons.codec.binary.Base64; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +public class JavaSecurityPemUtils { + + public static RSAPrivateKey readPKCS8PrivateKey(File file) throws GeneralSecurityException, IOException { + 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); + } + + public static RSAPublicKey readX509PublicKey(File file) throws GeneralSecurityException, IOException { + 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); + } + +} diff --git a/core-java-modules/core-java-security-2/src/main/resources/pem/private-key-pkcs8.pem b/core-java-modules/core-java-security-2/src/main/resources/pem/private-key-pkcs8.pem new file mode 100644 index 0000000000..903f903d7a --- /dev/null +++ b/core-java-modules/core-java-security-2/src/main/resources/pem/private-key-pkcs8.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCyO0YiTxLEP44S +IGk/b9MlQAXS6nC4oYyTrAfxHCi/zxW/MmtWbY0K2JxOTkVSD5QbmvwkCutXi0k9 +EdDK+orAXg2KSy686O/cfIh/iho6FmNPyEOd7UF+/5wWpknrUaTQyMA2H9Pmr2/E +RH/tN1Q0cqmhFX41WUo3lsRT81DkVCNVeJx+zDGHpjp+XY8gWpPYJ+MP4WQE9TWJ +P2rIlgcDfwhG/A21yK0WAJ5nB0Y+jGI8+HVYdjxXGlRUG//YmxS2sH+sAhsapmjE +Aha+KMk972jVNjdWU7OT0BJnUB5q286Kv6INUnk6kqYufNzjpCAY9SyMjKjpKN71 +3Gka2gZBAgMBAAECggEAFlPam12wiik0EQ1CYhIOL3JvyFZaPKbwR2ebrxbJ/A1j +OgqE69TZgGxWWHDxui/9a9/kildb2CG40Q+0SllMnICrzZFRj5TWx5ZKOz//vRsk +4c/CuLwKInC/Cw9V30bhEM61VZJzJ0j/BWVXaU4vHEro+ScKIoDHDWOzwJiQn6m9 +C+Ti5lFpax3hx8ZrgPqmBCFYNvErrWkOr7mCYl0jS+E22c68yn8+LjdlF1LWUa6N +zutk3MPj5UwEyR0h7EZReCeGkPTMQNyOBhDcmAtlEno4fjtZzUDHRjh8/QpG1Mz/ +alavvrkjswc1DmRUOdgiYu+Waxan5noBhxEAvd/hyQKBgQDjYJD0n+m0tUrpNtX0 ++mdzHstClHrpx5oNxs4sIBjCoCwEXaSpeY8+JxCdnZ6n29mLZLq/wPXxZ3EJcOSZ +PYUvZJfV/IUvoLPFbtT3ILzDTcAAeHj2GAOpzYP8J1JSFsc78ZjKMF1XeNjXcq8T +XNXoWfY7N/fShoycVeG42JJCFwKBgQDIqvHL0QfJ8r6yM8Efj7Zq6Wa4C9okORes +8UVWfBoO6UOWvpK+D9IjnaEisJcnEalwNi8/eKudR9hfvmzATV+t3YJIgktto3TT +BWLsEyniNU4vSTl7GPBrV2xabWogbChlt7TXUfw6YogaBKm43snYXBbJFc+NcpQH +ONB5igppZwKBgGDyYHvc3wGsttb/CXTde1RLUfD+a/XXpCixlmCcAtKhBoOKBdY4 +vUmL0HrTpLz/cR8NAM8XkAWwzDJxTxbDc1EEu/SCKatoAp5wph8Ed1dyhCXvN+v9 +yzoQJXFStrfHfIVjenji7DmKjjI2dM11rMLX8LPJJkI+Gh/iQk7VEG9bAoGAH/aS +sztleTZwR6RUw7k5fkgVM4W3xoNNkR+RQthbsjpXqMBMUXflqgSmsQbd3LxEd/o5 +hmurMk9KWN3VJsBsWB5rbS9L4nfh2OcHvcDDsCN7g66vODtduEthl/nLqMRxnton +NRD7EzW0pihN/IOINS1d98PAnrA8gfX7xxBE3ksCgYBvoljHGjvy3bPJ++vDGKJK +y6JuEeRVzgdPXEb60uU+BR7kdh+MMsZLmgfFTgza3R+/xeZcC/cuOPsbzeooRQi/ +9NpKwSCXjVNk9nglUWBoPRh4uYqrArWn+HoR7MI/BxeRJm5e1+ii8P19Y9joX5s0 +Q3OLn8GeH56ClJmNiWDhsA== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/core-java-modules/core-java-security-2/src/main/resources/pem/public-key.pem b/core-java-modules/core-java-security-2/src/main/resources/pem/public-key.pem new file mode 100644 index 0000000000..54262dca03 --- /dev/null +++ b/core-java-modules/core-java-security-2/src/main/resources/pem/public-key.pem @@ -0,0 +1,9 @@ +-----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----- \ No newline at end of file diff --git a/core-java-modules/core-java-security-2/src/test/java/com/baeldung/pem/JavaSecurityPemUtilsUnitTest.java b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/pem/JavaSecurityPemUtilsUnitTest.java new file mode 100644 index 0000000000..9c6db9c122 --- /dev/null +++ b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/pem/JavaSecurityPemUtilsUnitTest.java @@ -0,0 +1,33 @@ +package com.baeldung.pem; + + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class JavaSecurityPemUtilsUnitTest { + + @Test + public void whenReadPublicKeyFromPEMFile_thenSuccess() throws Exception { + File pemFile = new File(JavaSecurityPemUtilsUnitTest.class.getResource("/pem/public-key.pem").getFile()); + + RSAPublicKey publicKey = JavaSecurityPemUtils.readX509PublicKey(pemFile); + + assertEquals("X.509", publicKey.getFormat()); + assertEquals("RSA", publicKey.getAlgorithm()); + } + + @Test + public void whenReadPrivateKeyFromPEMFile_thenSuccess() throws Exception { + File pemFile = new File(JavaSecurityPemUtilsUnitTest.class.getResource("/pem/private-key-pkcs8.pem").getFile()); + + RSAPrivateKey privateKey = JavaSecurityPemUtils.readPKCS8PrivateKey(pemFile); + + assertEquals("PKCS#8", privateKey.getFormat()); + assertEquals("RSA", privateKey.getAlgorithm()); + } +} diff --git a/libraries-security/src/main/java/com/baeldung/pem/BouncyCastlePemUtils.java b/libraries-security/src/main/java/com/baeldung/pem/BouncyCastlePemUtils.java new file mode 100644 index 0000000000..03140666f7 --- /dev/null +++ b/libraries-security/src/main/java/com/baeldung/pem/BouncyCastlePemUtils.java @@ -0,0 +1,71 @@ +package com.baeldung.pem; + +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +public class BouncyCastlePemUtils { + + public static RSAPublicKey readX509PublicKey(File file) throws InvalidKeySpecException, IOException, NoSuchAlgorithmException { + 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); + } + } + + public static RSAPublicKey readX509PublicKeySecondApproach(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); + } + } + + public static RSAPrivateKey readPKCS8PrivateKey(File file) throws InvalidKeySpecException, IOException, NoSuchAlgorithmException { + 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); + } + } + + public static RSAPrivateKey readPKCS8PrivateKeySecondApproach(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); + } + } + +} diff --git a/libraries-security/src/main/resources/pem/private-key-pkcs8.pem b/libraries-security/src/main/resources/pem/private-key-pkcs8.pem new file mode 100644 index 0000000000..903f903d7a --- /dev/null +++ b/libraries-security/src/main/resources/pem/private-key-pkcs8.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCyO0YiTxLEP44S +IGk/b9MlQAXS6nC4oYyTrAfxHCi/zxW/MmtWbY0K2JxOTkVSD5QbmvwkCutXi0k9 +EdDK+orAXg2KSy686O/cfIh/iho6FmNPyEOd7UF+/5wWpknrUaTQyMA2H9Pmr2/E +RH/tN1Q0cqmhFX41WUo3lsRT81DkVCNVeJx+zDGHpjp+XY8gWpPYJ+MP4WQE9TWJ +P2rIlgcDfwhG/A21yK0WAJ5nB0Y+jGI8+HVYdjxXGlRUG//YmxS2sH+sAhsapmjE +Aha+KMk972jVNjdWU7OT0BJnUB5q286Kv6INUnk6kqYufNzjpCAY9SyMjKjpKN71 +3Gka2gZBAgMBAAECggEAFlPam12wiik0EQ1CYhIOL3JvyFZaPKbwR2ebrxbJ/A1j +OgqE69TZgGxWWHDxui/9a9/kildb2CG40Q+0SllMnICrzZFRj5TWx5ZKOz//vRsk +4c/CuLwKInC/Cw9V30bhEM61VZJzJ0j/BWVXaU4vHEro+ScKIoDHDWOzwJiQn6m9 +C+Ti5lFpax3hx8ZrgPqmBCFYNvErrWkOr7mCYl0jS+E22c68yn8+LjdlF1LWUa6N +zutk3MPj5UwEyR0h7EZReCeGkPTMQNyOBhDcmAtlEno4fjtZzUDHRjh8/QpG1Mz/ +alavvrkjswc1DmRUOdgiYu+Waxan5noBhxEAvd/hyQKBgQDjYJD0n+m0tUrpNtX0 ++mdzHstClHrpx5oNxs4sIBjCoCwEXaSpeY8+JxCdnZ6n29mLZLq/wPXxZ3EJcOSZ +PYUvZJfV/IUvoLPFbtT3ILzDTcAAeHj2GAOpzYP8J1JSFsc78ZjKMF1XeNjXcq8T +XNXoWfY7N/fShoycVeG42JJCFwKBgQDIqvHL0QfJ8r6yM8Efj7Zq6Wa4C9okORes +8UVWfBoO6UOWvpK+D9IjnaEisJcnEalwNi8/eKudR9hfvmzATV+t3YJIgktto3TT +BWLsEyniNU4vSTl7GPBrV2xabWogbChlt7TXUfw6YogaBKm43snYXBbJFc+NcpQH +ONB5igppZwKBgGDyYHvc3wGsttb/CXTde1RLUfD+a/XXpCixlmCcAtKhBoOKBdY4 +vUmL0HrTpLz/cR8NAM8XkAWwzDJxTxbDc1EEu/SCKatoAp5wph8Ed1dyhCXvN+v9 +yzoQJXFStrfHfIVjenji7DmKjjI2dM11rMLX8LPJJkI+Gh/iQk7VEG9bAoGAH/aS +sztleTZwR6RUw7k5fkgVM4W3xoNNkR+RQthbsjpXqMBMUXflqgSmsQbd3LxEd/o5 +hmurMk9KWN3VJsBsWB5rbS9L4nfh2OcHvcDDsCN7g66vODtduEthl/nLqMRxnton +NRD7EzW0pihN/IOINS1d98PAnrA8gfX7xxBE3ksCgYBvoljHGjvy3bPJ++vDGKJK +y6JuEeRVzgdPXEb60uU+BR7kdh+MMsZLmgfFTgza3R+/xeZcC/cuOPsbzeooRQi/ +9NpKwSCXjVNk9nglUWBoPRh4uYqrArWn+HoR7MI/BxeRJm5e1+ii8P19Y9joX5s0 +Q3OLn8GeH56ClJmNiWDhsA== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/libraries-security/src/main/resources/pem/public-key.pem b/libraries-security/src/main/resources/pem/public-key.pem new file mode 100644 index 0000000000..54262dca03 --- /dev/null +++ b/libraries-security/src/main/resources/pem/public-key.pem @@ -0,0 +1,9 @@ +-----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----- \ No newline at end of file diff --git a/libraries-security/src/test/java/com/baeldung/pem/BouncyCastlePemUtilsUnitTest.java b/libraries-security/src/test/java/com/baeldung/pem/BouncyCastlePemUtilsUnitTest.java new file mode 100644 index 0000000000..1297568d0e --- /dev/null +++ b/libraries-security/src/test/java/com/baeldung/pem/BouncyCastlePemUtilsUnitTest.java @@ -0,0 +1,41 @@ +package com.baeldung.pem; + + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BouncyCastlePemUtilsUnitTest { + + @Test + public void whenReadPublicKeyFromPEMFile_thenSuccess() throws Exception { + File pemFile = new File(BouncyCastlePemUtilsUnitTest.class.getResource("/pem/public-key.pem").getFile()); + + RSAPublicKey publicKey1 = BouncyCastlePemUtils.readX509PublicKey(pemFile); + RSAPublicKey publicKey2 = BouncyCastlePemUtils.readX509PublicKeySecondApproach(pemFile); + + assertEquals("X.509", publicKey1.getFormat()); + assertEquals("RSA", publicKey1.getAlgorithm()); + + assertEquals("X.509", publicKey2.getFormat()); + assertEquals("RSA", publicKey2.getAlgorithm()); + } + + @Test + public void whenReadPrivateKeyFromPEMFile_thenSuccess() throws Exception { + File pemFile = new File(BouncyCastlePemUtilsUnitTest.class.getResource("/pem/private-key-pkcs8.pem").getFile()); + + RSAPrivateKey privateKey1 = BouncyCastlePemUtils.readPKCS8PrivateKey(pemFile); + RSAPrivateKey privateKey2 = BouncyCastlePemUtils.readPKCS8PrivateKeySecondApproach(pemFile); + + assertEquals("PKCS#8", privateKey1.getFormat()); + assertEquals("RSA", privateKey1.getAlgorithm()); + + assertEquals("PKCS#8", privateKey2.getFormat()); + assertEquals("RSA", privateKey2.getAlgorithm()); + } +}