From 580cfc970a7e2caf2bfe801a606c79ef906bea09 Mon Sep 17 00:00:00 2001 From: Andrey Pleskach Date: Tue, 20 Jun 2023 08:20:19 +0200 Subject: [PATCH] Use boucycastle PEM reader instead of reg expression Use BouncyCastle PEMReader instead of regular expression to read and parse private key pem files. Signed-off-by: Andrey Pleskach --- .../security/support/PemKeyReader.java | 89 +++++-------------- .../http/saml/HTTPSamlAuthenticatorTest.java | 26 +++--- 2 files changed, 36 insertions(+), 79 deletions(-) diff --git a/src/main/java/org/opensearch/security/support/PemKeyReader.java b/src/main/java/org/opensearch/security/support/PemKeyReader.java index 53eeb21736..01f8971d3d 100644 --- a/src/main/java/org/opensearch/security/support/PemKeyReader.java +++ b/src/main/java/org/opensearch/security/support/PemKeyReader.java @@ -30,9 +30,9 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -53,8 +53,6 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Collection; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.crypto.Cipher; import javax.crypto.EncryptedPrivateKeyInfo; @@ -65,7 +63,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; import org.opensearch.OpenSearchException; import org.opensearch.common.settings.Settings; @@ -73,81 +72,37 @@ public final class PemKeyReader { - //private static final String[] EMPTY_STRING_ARRAY = new String[0]; - protected static final Logger log = LogManager.getLogger(PemKeyReader.class); + private static final Logger log = LogManager.getLogger(PemKeyReader.class); static final String JKS = "JKS"; static final String PKCS12 = "PKCS12"; - - private static final Pattern KEY_PATTERN = Pattern.compile( - "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header - "([a-z0-9+/=\\r\\n]+)" + // Base64 text - "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer - Pattern.CASE_INSENSITIVE); private static byte[] readPrivateKey(File file) throws KeyException { - try { - InputStream in = new FileInputStream(file); - - try { - return readPrivateKey(in); - } finally { - safeClose(in); - } - } catch (FileNotFoundException e) { + try (final InputStream in = new FileInputStream(file)) { + return readPrivateKey(in); + } catch (final IOException e) { throw new KeyException("could not fine key file: " + file); } } - private static byte[] readPrivateKey(InputStream in) throws KeyException { - String content; - try { - content = readContent(in); - } catch (IOException e) { - throw new KeyException("failed to read key input stream", e); - } - - Matcher m = KEY_PATTERN.matcher(content); - if (!m.find()) { - throw new KeyException("could not find a PKCS #8 private key in input stream" + - " (see http://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)"); - } - - return Base64.decode(m.group(1)); - } - - private static String readContent(InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - byte[] buf = new byte[8192]; - for (;;) { - int ret = in.read(buf); - if (ret < 0) { - break; - } - out.write(buf, 0, ret); + private static byte[] readPrivateKey(final InputStream in) throws KeyException { + try (final PemReader pemReader = new PemReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + final PemObject pemObject = pemReader.readPemObject(); + if (pemObject == null) { + throw new KeyException( + "could not find a PKCS #8 private key in input stream" + + " (see http://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)" + ); } - return out.toString(StandardCharsets.US_ASCII.name()); - } finally { - safeClose(out); + return pemObject.getContent(); + } catch (final IOException ioe) { + throw new KeyException( + "could not find a PKCS #8 private key in input stream" + + " (see http://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)", + ioe + ); } } - private static void safeClose(InputStream in) { - try { - in.close(); - } catch (IOException e) { - //ignore - } - } - - private static void safeClose(OutputStream out) { - try { - out.close(); - } catch (IOException e) { - //ignore - } - } - public static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, KeyException, IOException { if (keyFile == null) { diff --git a/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java b/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java index bfaf33049d..f4cabbfc66 100644 --- a/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java +++ b/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java @@ -94,6 +94,8 @@ public class HTTPSamlAuthenticatorTest { + "XXIa8gT/MdNT0+W3NHKcbE31pDhOI92COZWlhOyp1cLhyo1ytayjxPTl/2RM/Vtj\n" + "T9IKkp7810LOKhrCDQ==\n" + "-----END ENCRYPTED PRIVATE KEY-----"; + private final static String PRIVATE_KEY_PATTERN = "-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----"; + private static X509Certificate spSigningCertificate; private static PrivateKey spSigningPrivateKey; @@ -159,8 +161,8 @@ public void decryptAssertionsTest() throws Exception { Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") + .put("sp.signature_private_key", + String.format(PRIVATE_KEY_PATTERN, Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()))) .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -198,8 +200,8 @@ public void shouldUnescapeSamlEntitiesTest() throws Exception { Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") + .put("sp.signature_private_key", + String.format(PRIVATE_KEY_PATTERN, Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()))) .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -240,8 +242,8 @@ public void shouldUnescapeSamlEntitiesTest2() throws Exception { Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") + .put("sp.signature_private_key", + String.format(PRIVATE_KEY_PATTERN, Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()))) .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -282,8 +284,8 @@ public void shouldNotEscapeSamlEntities() throws Exception { Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") + .put("sp.signature_private_key", + String.format(PRIVATE_KEY_PATTERN, Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()))) .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -324,8 +326,8 @@ public void shouldNotTrimWhitespaceInJwtRoles() throws Exception { Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") + .put("sp.signature_private_key", + String.format(PRIVATE_KEY_PATTERN, Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()))) .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -663,8 +665,8 @@ public void basicLogoutTest() throws Exception { Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) .put("exchange_key", "abc").put("roles_key", "roles") - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") + .put("sp.signature_private_key", + String.format(PRIVATE_KEY_PATTERN, Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()))) .put("path.home", ".").build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null);