You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2022/09/09 16:23:18 UTC

[tomee] 02/03: Parsing for private decrypt keys

This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git

commit b682e9c48f0702e640bfd72bdc130fb449a65e9e
Author: David Blevins <db...@tomitribe.com>
AuthorDate: Thu Sep 8 20:08:25 2022 -0700

    Parsing for private decrypt keys
---
 .../microprofile/jwt/JsonWebTokenValidator.java    |   4 +-
 .../jwt/config/JWTAuthConfigurationProperties.java |   2 +-
 .../{PublicKeyResolver.java => KeyResolver.java}   |  40 ++++--
 .../microprofile/jwt/config/KeyResolverTest.java   | 153 +++++++++++++++++++++
 .../jwt/config/PublicKeyResolverTest.java          |  78 -----------
 5 files changed, 187 insertions(+), 90 deletions(-)

diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java
index 40d2eec817..a0caf6d096 100644
--- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java
+++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java
@@ -18,7 +18,7 @@ package org.apache.tomee.microprofile.jwt;
 
 import org.apache.openejb.util.Logger;
 import org.apache.tomee.microprofile.jwt.config.JWTAuthConfiguration;
-import org.apache.tomee.microprofile.jwt.config.PublicKeyResolver;
+import org.apache.tomee.microprofile.jwt.config.KeyResolver;
 import org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipal;
 import org.eclipse.microprofile.jwt.Claims;
 import org.eclipse.microprofile.jwt.JsonWebToken;
@@ -136,7 +136,7 @@ public class JsonWebTokenValidator {
         }
 
         public Builder publicKey(final String keyContent) {
-            final Map<String, Key> keys = new PublicKeyResolver().readPublicKeys(keyContent);
+            final Map<String, Key> keys = new KeyResolver().readPublicKeys(keyContent);
             final Map.Entry<String, Key> key = keys.entrySet().iterator().next();
             return verificationKey(key.getValue());
         }
diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java
index f5255bb428..1697c92c7e 100644
--- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java
+++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java
@@ -92,7 +92,7 @@ public class JWTAuthConfigurationProperties {
         final Optional<String> publicKeyLocation = getPublicKeyLocation();
         final List<String> audiences = getAudiences();
 
-        final Map<String, Key> keys = new PublicKeyResolver().resolve(publicKeyContents, publicKeyLocation).orElse(null);
+        final Map<String, Key> keys = new KeyResolver().resolvePublicKey(publicKeyContents, publicKeyLocation).orElse(null);
         final Boolean allowNoExp = config.getOptionalValue("mp.jwt.tomee.allow.no-exp", Boolean.class).orElse(false);
 
         return JWTAuthConfiguration.authConfiguration(keys, getIssuer().orElse(null), allowNoExp, audiences.toArray(new String[0]));
diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/PublicKeyResolver.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/KeyResolver.java
similarity index 79%
rename from mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/PublicKeyResolver.java
rename to mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/KeyResolver.java
index a32e669473..d9e73bfd7b 100644
--- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/PublicKeyResolver.java
+++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/KeyResolver.java
@@ -17,7 +17,6 @@
 package org.apache.tomee.microprofile.jwt.config;
 
 import io.churchkey.Keys;
-import io.churchkey.util.Pem;
 import jakarta.enterprise.inject.spi.DeploymentException;
 import org.apache.openejb.loader.IO;
 
@@ -31,18 +30,28 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 import static io.churchkey.Key.Type.PRIVATE;
+import static io.churchkey.Key.Type.PUBLIC;
 import static io.churchkey.Key.Type.SECRET;
 
-public class PublicKeyResolver {
+public class KeyResolver {
 
-    public Optional<Map<String, Key>> resolve(final Optional<String> publicKeyContents, final Optional<String> publicKeyLocation) {
+    public Optional<Map<String, Key>> resolvePublicKey(final Optional<String> keyContents, final Optional<String> keyLocation) {
+        return resolve(keyContents, keyLocation, this::validatePublicKeys);
+    }
+
+    public Optional<Map<String, Key>> resolveDecryptKey(final Optional<String> keyContents, final Optional<String> keyLocation) {
+        return resolve(keyContents, keyLocation, this::validateDecryptKeys);
+    }
+
+    private Optional<Map<String, Key>> resolve(final Optional<String> publicKeyContents, final Optional<String> publicKeyLocation, final Consumer<List<io.churchkey.Key>> validation) {
         final Stream<Supplier<Optional<Map<String, Key>>>> possiblePublicKeys =
-                Stream.of(() -> publicKeyContents.map(this::readPublicKeys),
-                        () -> publicKeyLocation.map(this::readPublicKeysFromLocation));
+                Stream.of(() -> publicKeyContents.map(publicKey -> readPublicKeys(publicKey, validation)),
+                        () -> publicKeyLocation.map(publicKeyLocation1 -> readPublicKeysFromLocation(publicKeyLocation1, validation)));
 
         return (Optional<Map<String, Key>>) possiblePublicKeys
                 .map(Supplier::get)
@@ -52,6 +61,10 @@ public class PublicKeyResolver {
     }
 
     public Map<String, Key> readPublicKeys(final String publicKey) {
+        return readPublicKeys(publicKey, this::validatePublicKeys);
+    }
+
+    public Map<String, Key> readPublicKeys(final String publicKey, final Consumer<List<io.churchkey.Key>> validation) {
         final List<io.churchkey.Key> keys;
         try {
             keys = Keys.decodeSet(publicKey);
@@ -63,8 +76,7 @@ public class PublicKeyResolver {
             throw new DeploymentException("No keys found in key contents: " + publicKey);
         }
 
-        checkInvalidTypes(keys, PRIVATE);
-        checkInvalidTypes(keys, SECRET);
+        validation.accept(keys);
 
         int unknown = 0;
 
@@ -85,6 +97,15 @@ public class PublicKeyResolver {
         return map;
     }
 
+    private void validatePublicKeys(final List<io.churchkey.Key> keys) {
+        checkInvalidTypes(keys, PRIVATE);
+        checkInvalidTypes(keys, SECRET);
+    }
+
+    private void validateDecryptKeys(final List<io.churchkey.Key> keys) {
+        checkInvalidTypes(keys, PUBLIC);
+    }
+
     private boolean defined(final io.churchkey.Key key, final String kid) {
         final String attribute = key.getAttribute(kid);
         return attribute != null && attribute.length() > 0;
@@ -102,7 +123,7 @@ public class PublicKeyResolver {
         }
     }
 
-    private Map<String, Key> readPublicKeysFromLocation(final String publicKeyLocation) {
+    private Map<String, Key> readPublicKeysFromLocation(final String publicKeyLocation, final Consumer<List<io.churchkey.Key>> validatePublicKeys) {
         final Stream<Supplier<Optional<String>>> possiblePublicKeysLocations =
                 Stream.of(() -> readPublicKeysFromClasspath(publicKeyLocation),
                         () -> readPublicKeysFromFile(publicKeyLocation),
@@ -113,7 +134,7 @@ public class PublicKeyResolver {
                 .filter(Optional::isPresent)
                 .map(Optional::get)
                 .findFirst()
-                .map(this::readPublicKeys)
+                .map(publicKey -> readPublicKeys(publicKey, validatePublicKeys))
                 .orElseThrow(() -> new DeploymentException(JWTAuthConfigurationProperties.PUBLIC_KEY_ERROR_LOCATION +
                         publicKeyLocation));
     }
@@ -164,4 +185,5 @@ public class PublicKeyResolver {
         }
     }
 
+
 }
diff --git a/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/config/KeyResolverTest.java b/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/config/KeyResolverTest.java
new file mode 100644
index 0000000000..28614c7290
--- /dev/null
+++ b/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/config/KeyResolverTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.microprofile.jwt.config;
+
+import io.churchkey.Key;
+import io.churchkey.Keys;
+import jakarta.enterprise.inject.spi.DeploymentException;
+import org.apache.openejb.loader.Files;
+import org.apache.openejb.loader.IO;
+import org.apache.tomee.microprofile.jwt.KeyAsserts;
+import org.junit.Test;
+
+import java.io.File;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class KeyResolverTest {
+
+    @Test
+    public void publicKeyPemFromFileUrl() throws Exception {
+        final File dir = Files.tmpdir();
+        final Key privateKey = generate(Key.Algorithm.RSA);
+        final Key expected = privateKey.getPublicKey();
+
+        final File file = new File(dir, "publicKey.pem");
+        IO.copy(expected.encode(Key.Format.PEM), file);
+
+        final Map<String, java.security.Key> keys = new KeyResolver().resolvePublicKey(
+                Optional.empty(),
+                Optional.of(file.toURI().toASCIIString())).get();
+
+        assertEquals(1, keys.size());
+        final java.security.Key actual = keys.values().iterator().next();
+
+        KeyAsserts.assertRsaPublicKey((RSAPublicKey) expected.getKey(), (RSAPublicKey) actual);
+    }
+
+    @Test
+    public void publicKeyPemFromFileUrlInvalidKey() throws Exception {
+        final File dir = Files.tmpdir();
+        final Key expected = generate(Key.Algorithm.RSA);
+
+        final File file = new File(dir, "publicKey.pem");
+        IO.copy(expected.encode(Key.Format.PEM), file);
+
+        try {
+            new KeyResolver().resolvePublicKey(
+                    Optional.empty(),
+                    Optional.of(file.toURI().toASCIIString())).get();
+            fail("Expected DeploymentException");
+        } catch (DeploymentException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void publicKeyPemContents() throws Exception {
+        final Key privateKey = generate(Key.Algorithm.RSA);
+        final Key expected = privateKey.getPublicKey();
+
+        final Map<String, java.security.Key> keys = new KeyResolver().resolvePublicKey(
+                Optional.of(expected.toPem()),
+                Optional.empty()).get();
+
+        assertEquals(1, keys.size());
+        final java.security.Key actual = keys.values().iterator().next();
+
+        KeyAsserts.assertRsaPublicKey((RSAPublicKey) expected.getKey(), (RSAPublicKey) actual);
+    }
+
+    @Test
+    public void privateKeyPemFromFileUrl() throws Exception {
+        final File dir = Files.tmpdir();
+        final Key expected = generate(Key.Algorithm.RSA);
+
+        final File file = new File(dir, "privateKey.pem");
+        IO.copy(expected.encode(Key.Format.PEM), file);
+
+        final Map<String, java.security.Key> keys = new KeyResolver().resolveDecryptKey(
+                Optional.empty(),
+                Optional.of(file.toURI().toASCIIString())).get();
+
+        assertEquals(1, keys.size());
+        final java.security.Key actual = keys.values().iterator().next();
+
+        KeyAsserts.assertRsaPrivateKey((RSAPrivateCrtKey) expected.getKey(), (RSAPrivateCrtKey) actual);
+    }
+
+    @Test
+    public void privateKeyPemFromFileUrlInvalidKey() throws Exception {
+        final File dir = Files.tmpdir();
+        final Key expected = generate(Key.Algorithm.RSA).getPublicKey();
+
+        final File file = new File(dir, "publicKey.pem");
+        IO.copy(expected.encode(Key.Format.PEM), file);
+
+        try {
+            new KeyResolver().resolveDecryptKey(
+                    Optional.empty(),
+                    Optional.of(file.toURI().toASCIIString())).get();
+            fail("Expected DeploymentException");
+        } catch (DeploymentException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void privateKeyJwkFromFileUrl() throws Exception {
+        final File dir = Files.tmpdir();
+        final Key expected = generate(Key.Algorithm.RSA);
+
+        final File file = new File(dir, "privateKey.jwk");
+        IO.copy(expected.encode(Key.Format.JWK), file);
+
+        final Map<String, java.security.Key> keys = new KeyResolver().resolveDecryptKey(
+                Optional.empty(),
+                Optional.of(file.toURI().toASCIIString())).get();
+
+        assertEquals(1, keys.size());
+        final java.security.Key actual = keys.values().iterator().next();
+
+        KeyAsserts.assertRsaPrivateKey((RSAPrivateCrtKey) expected.getKey(), (RSAPrivateCrtKey) actual);
+    }
+
+    private Key generate(final Key.Algorithm algorithm) throws NoSuchAlgorithmException {
+        final KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm.name());
+        final KeyPair pair = generator.generateKeyPair();
+        return Keys.of(pair);
+    }
+
+}
diff --git a/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/config/PublicKeyResolverTest.java b/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/config/PublicKeyResolverTest.java
deleted file mode 100644
index e792870ee9..0000000000
--- a/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/config/PublicKeyResolverTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.tomee.microprofile.jwt.config;
-
-import io.churchkey.Key;
-import io.churchkey.Keys;
-import org.apache.openejb.loader.Files;
-import org.apache.openejb.loader.IO;
-import org.apache.tomee.microprofile.jwt.KeyAsserts;
-import org.junit.Test;
-
-import java.io.File;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Map;
-import java.util.Optional;
-
-import static org.junit.Assert.assertEquals;
-
-public class PublicKeyResolverTest {
-
-    @Test
-    public void publicKeyPemFromFileUrl() throws Exception {
-        final File dir = Files.tmpdir();
-        final Key privateKey = generate(Key.Algorithm.RSA);
-        final Key expected = privateKey.getPublicKey();
-
-        final File file = new File(dir, "publicKey.pem");
-        IO.copy(expected.encode(Key.Format.PEM), file);
-
-        final Map<String, java.security.Key> keys = new PublicKeyResolver().resolve(
-                Optional.empty(),
-                Optional.of(file.toURI().toASCIIString())).get();
-
-        assertEquals(1, keys.size());
-        final java.security.Key actual = keys.values().iterator().next();
-
-        KeyAsserts.assertRsaPublicKey((RSAPublicKey) expected.getKey(), (RSAPublicKey) actual);
-    }
-
-    @Test
-    public void publicKeyPemContents() throws Exception {
-        final Key privateKey = generate(Key.Algorithm.RSA);
-        final Key expected = privateKey.getPublicKey();
-
-        final Map<String, java.security.Key> keys = new PublicKeyResolver().resolve(
-                Optional.of(expected.toPem()),
-                Optional.empty()).get();
-
-        assertEquals(1, keys.size());
-        final java.security.Key actual = keys.values().iterator().next();
-
-        KeyAsserts.assertRsaPublicKey((RSAPublicKey) expected.getKey(), (RSAPublicKey) actual);
-    }
-
-    private Key generate(final Key.Algorithm algorithm) throws NoSuchAlgorithmException {
-        final KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm.name());
-        final KeyPair pair = generator.generateKeyPair();
-        return Keys.of(pair);
-    }
-
-}