From 0992f8bda5660ca6c667b6531cfa686990c95e59 Mon Sep 17 00:00:00 2001 From: Harry Kodden Date: Tue, 29 Apr 2025 12:41:20 +0000 Subject: [PATCH 1/6] add support for EC Public Keys --- .../org/dcache/gplazma/oidc/jwt/Issuer.java | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/jwt/Issuer.java b/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/jwt/Issuer.java index 27528a51169..3648b27e853 100644 --- a/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/jwt/Issuer.java +++ b/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/jwt/Issuer.java @@ -31,8 +31,14 @@ import java.security.PublicKey; import java.security.spec.KeySpec; import java.security.spec.RSAPublicKeySpec; -import java.time.Duration; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECPoint; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.ECGenParameterSpec; +import java.security.AlgorithmParameters; import java.util.Base64; +import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -214,6 +220,8 @@ private PublicKey buildPublicKey(JsonNode details) throws BadKeyDescriptionExcep switch (kty) { case "RSA": return buildRSAPublicKey(details); + case "EC": + return buildECPublicKey(details); default: throw new BadKeyDescriptionException("Unknown key type " + kty); } @@ -249,6 +257,41 @@ private PublicKey buildRSAPublicKey(JsonNode details) throws BadKeyDescriptionEx } } + private ECParameterSpec getECParameterSpec(String crv) throws GeneralSecurityException { + String name; + + switch (crv) { + case "P-256": + name = "secp256r1"; + break; + case "P-384": + name = "secp384r1"; + break; + case "P-521": + name = "secp521r1"; + break; + default: + throw new GeneralSecurityException("Unsupported curve: " + crv); + } + AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC"); + parameters.init(new ECGenParameterSpec(name)); + return parameters.getParameterSpec(ECParameterSpec.class); + } + + private PublicKey buildECPublicKey(JsonNode details) throws BadKeyDescriptionException { + try { + String crv = getString(details, "crv"); + byte[] x = Base64.getUrlDecoder().decode(getString(details, "x")); + byte[] y = Base64.getUrlDecoder().decode(getString(details, "y")); + ECParameterSpec params = getECParameterSpec(crv); + ECPoint point = new ECPoint(new BigInteger(1, x), new BigInteger(1, y)); + ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, params); + return KeyFactory.getInstance("EC").generatePublic(pubSpec); + } catch (GeneralSecurityException e) { + throw new BadKeyDescriptionException("Unable to build EC public key: " + e.toString()); + } + } + public void checkIssued(JsonWebToken token) throws AuthenticationException { Map> keyMap = keys.get() .orElseThrow(msg -> new AuthenticationException( From 2d22189329337d267c743cd9ef5fce546fc78497 Mon Sep 17 00:00:00 2001 From: Harry Kodden Date: Tue, 29 Apr 2025 15:31:44 +0200 Subject: [PATCH 2/6] Update JsonWebToken.java Add support for EC Signing Algorithms --- .../src/main/java/org/dcache/gplazma/util/JsonWebToken.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java index f7b12f52f83..efac12e6e30 100644 --- a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java +++ b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java @@ -129,6 +129,12 @@ private Signature getSignature() throws GeneralSecurityException { switch (alg) { case "RS256": return Signature.getInstance("SHA256withRSA"); + case "ES256": + return Signature.getInstance("SHA256withECDSA"); + case "ES384": + return Signature.getInstance("SHA384withECDSA"); + case "ES512": + return Signature.getInstance("SHA512withECDSA"); default: throw new NoSuchAlgorithmException("Unknown JWT alg " + alg); } From 02e99fc200abaa16b46e388bb5c73f8323a5978a Mon Sep 17 00:00:00 2001 From: Harry Kodden Date: Tue, 29 Apr 2025 15:57:48 +0200 Subject: [PATCH 3/6] Update JsonWebToken.java EC Signatures to be transcoded to DER format --- .../org/dcache/gplazma/util/JsonWebToken.java | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java index efac12e6e30..cba02c93739 100644 --- a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java +++ b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java @@ -113,12 +113,54 @@ public String getKeyIdentifier() { return kid; } + private static byte[] transcodeJWTECDSASignatureToDER(byte[] jwsSignature) throws SignatureException { + int rawLen = jwsSignature.length / 2; + + // Find the start of R (skip leading zeros) + int rStart = 0; + while (rStart < rawLen && jwsSignature[rStart] == 0) { + rStart++; + } + int rLen = rawLen - rStart; + + // Find the start of S (skip leading zeros) + int sStart = rawLen; + while (sStart < jwsSignature.length && jwsSignature[sStart] == 0) { + sStart++; + } + int sLen = rawLen - (sStart - rawLen); + + int totalLen = 2 + 2 + rLen + 2 + sLen; // SEQUENCE + INTEGER(R) + INTEGER(S) + int offset = 0; + byte[] der = new byte[totalLen]; + + der[offset++] = 0x30; // SEQUENCE + der[offset++] = (byte) (totalLen - 2); + + // INTEGER R + der[offset++] = 0x02; + der[offset++] = (byte) rLen; + System.arraycopy(jwsSignature, rawLen - rLen, der, offset, rLen); + offset += rLen; + + // INTEGER S + der[offset++] = 0x02; + der[offset++] = (byte) sLen; + System.arraycopy(jwsSignature, jwsSignature.length - sLen, der, offset, sLen); + + return der; + } + public boolean isSignedBy(PublicKey key) { try { Signature signature = getSignature(); signature.initVerify(key); signature.update(unsignedToken); - return signature.verify(this.signature); + byte[] sig = this.signature; + if (alg.startsWith("ES")) { + sig = transcodeJWTECDSASignatureToDER(sig); + } + return signature.verify(sig); } catch (GeneralSecurityException e) { LOGGER.warn("Problem verifying signature: {}", e.toString()); return false; From 6e5c91b36ef239e31fa2d2845913662607d2a1c8 Mon Sep 17 00:00:00 2001 From: Harry Kodden Date: Tue, 29 Apr 2025 16:04:03 +0200 Subject: [PATCH 4/6] Update JsonWebToken.java Adjust Signature Exception catching --- .../src/main/java/org/dcache/gplazma/util/JsonWebToken.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java index cba02c93739..9b07fb3ea04 100644 --- a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java +++ b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java @@ -161,7 +161,7 @@ public boolean isSignedBy(PublicKey key) { sig = transcodeJWTECDSASignatureToDER(sig); } return signature.verify(sig); - } catch (GeneralSecurityException e) { + } catch (GeneralSecurityException | SignatureException e) { LOGGER.warn("Problem verifying signature: {}", e.toString()); return false; } From 5155d12c7d3edd16630ea1149553aa86888e68d0 Mon Sep 17 00:00:00 2001 From: Harry Kodden Date: Tue, 29 Apr 2025 16:11:53 +0200 Subject: [PATCH 5/6] Update JsonWebToken.java Add: import java.security.SignatureException; --- .../src/main/java/org/dcache/gplazma/util/JsonWebToken.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java index 9b07fb3ea04..db4c98c0363 100644 --- a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java +++ b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java @@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; import java.security.PublicKey; import java.security.Signature; import java.time.Instant; From 4b771ef3f9f4820e4e7be682dad017a23dcb87fd Mon Sep 17 00:00:00 2001 From: Harry Kodden Date: Tue, 29 Apr 2025 16:25:03 +0200 Subject: [PATCH 6/6] Update JsonWebToken.java Adjust catching exceptions --- .../src/main/java/org/dcache/gplazma/util/JsonWebToken.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java index db4c98c0363..6997b9c5fac 100644 --- a/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java +++ b/modules/gplazma2/src/main/java/org/dcache/gplazma/util/JsonWebToken.java @@ -162,7 +162,10 @@ public boolean isSignedBy(PublicKey key) { sig = transcodeJWTECDSASignatureToDER(sig); } return signature.verify(sig); - } catch (GeneralSecurityException | SignatureException e) { + } catch (SignatureException e) { + LOGGER.warn("Problem verifying signature: {}", e.toString()); + return false; + } catch (GeneralSecurityException e) { LOGGER.warn("Problem verifying signature: {}", e.toString()); return false; }