-
Notifications
You must be signed in to change notification settings - Fork 2
feat(sdk): enhance assertion verification to support jwk and x509 certificates #322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d4220d5
1d4fae9
1532e06
6b2042f
092ab58
5e823d5
b898509
2ce0151
edb6d2e
249a3f8
b386c1f
5c97432
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,9 @@ | |
| import com.nimbusds.jose.crypto.MACVerifier; | ||
| import com.nimbusds.jose.crypto.RSASSASigner; | ||
| import com.nimbusds.jose.crypto.RSASSAVerifier; | ||
| import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory; | ||
| import com.nimbusds.jose.jwk.JWK; | ||
| import com.nimbusds.jose.util.X509CertUtils; | ||
| import com.nimbusds.jwt.JWTClaimsSet; | ||
| import com.nimbusds.jwt.SignedJWT; | ||
| import io.opentdf.platform.sdk.SDK.AssertionException; | ||
|
|
@@ -33,6 +36,7 @@ | |
| import java.security.NoSuchAlgorithmException; | ||
| import java.security.PrivateKey; | ||
| import java.security.interfaces.RSAPublicKey; | ||
| import java.security.cert.X509Certificate; | ||
| import java.text.ParseException; | ||
| import java.util.ArrayList; | ||
| import java.util.Base64; | ||
|
|
@@ -400,7 +404,7 @@ public void sign(final HashValues hashValues, final AssertionConfig.AssertionKey | |
| // returns the hash and the signature. It returns an error if the verification | ||
| // fails. | ||
| public Assertion.HashValues verify(AssertionConfig.AssertionKey assertionKey) | ||
| throws ParseException, JOSEException { | ||
| throws ParseException, JOSEException, java.security.cert.CertificateException { | ||
| if (binding == null) { | ||
| throw new AssertionException("Binding is null in assertion", this.id); | ||
| } | ||
|
|
@@ -409,7 +413,37 @@ public Assertion.HashValues verify(AssertionConfig.AssertionKey assertionKey) | |
| binding = null; // Clear the binding after use | ||
|
|
||
| SignedJWT signedJWT = SignedJWT.parse(signatureString); | ||
| JWSVerifier verifier = createVerifier(assertionKey); | ||
| JWSHeader header = signedJWT.getHeader(); | ||
| JWSVerifier verifier = null; | ||
|
|
||
| // Check for JWK in header | ||
| if (header.getJWK() != null) { | ||
| try { | ||
| verifier = createVerifier(header.getJWK()); | ||
| } catch (JOSEException e) { | ||
| throw new SDKException("Invalid JWK in JWT header", e); | ||
| } | ||
| } | ||
|
|
||
| // Check for X.509 certificate chain in header | ||
| if (verifier == null && header.getX509CertChain() != null && !header.getX509CertChain().isEmpty()) { | ||
| try { | ||
| X509Certificate cert = X509CertUtils.parse(header.getX509CertChain().get(0).decode()); | ||
| if (cert.getPublicKey() instanceof RSAPublicKey) { | ||
| verifier = createVerifier((RSAPublicKey) cert.getPublicKey()); | ||
| } else { | ||
| throw new SDKException("Unsupported public key type in X.509 certificate"); | ||
| } | ||
| } catch (IllegalArgumentException e) { | ||
| throw new SDKException("Invalid Base64 in X.509 certificate in JWT header", e); | ||
| } | ||
| } | ||
|
Comment on lines
+428
to
+440
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current implementation for handling the The certificate chain provided in I am not providing a code suggestion as a full implementation would require significant changes, including setting up a trust store. However, this vulnerability must be addressed. A possible approach would involve using |
||
|
|
||
|
|
||
| if (verifier == null) { | ||
| verifier = createVerifier(assertionKey); | ||
| } | ||
|
|
||
|
|
||
| if (!signedJWT.verify(verifier)) { | ||
| throw new SDKException("Unable to verify assertion signature"); | ||
|
|
@@ -424,19 +458,27 @@ public Assertion.HashValues verify(AssertionConfig.AssertionKey assertionKey) | |
|
|
||
| private SignedJWT createSignedJWT(final JWTClaimsSet claims, final AssertionConfig.AssertionKey assertionKey) | ||
| throws SDKException { | ||
| final JWSHeader jwsHeader; | ||
| final JWSHeader.Builder headerBuilder; | ||
| switch (assertionKey.alg) { | ||
| case RS256: | ||
| jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).build(); | ||
| headerBuilder = new JWSHeader.Builder(JWSAlgorithm.RS256); | ||
| break; | ||
| case HS256: | ||
| jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256).build(); | ||
| headerBuilder = new JWSHeader.Builder(JWSAlgorithm.HS256); | ||
| break; | ||
| default: | ||
| throw new SDKException("Unknown assertion key algorithm, error signing assertion"); | ||
| } | ||
|
|
||
| return new SignedJWT(jwsHeader, claims); | ||
| if (assertionKey.jwk != null) { | ||
| headerBuilder.jwk(assertionKey.jwk); | ||
| } | ||
|
|
||
| if (assertionKey.x5c != null) { | ||
| headerBuilder.x509CertChain(assertionKey.x5c); | ||
| } | ||
|
|
||
| return new SignedJWT(headerBuilder.build(), claims); | ||
| } | ||
|
|
||
| private JWSSigner createSigner(final AssertionConfig.AssertionKey assertionKey) | ||
|
|
@@ -460,13 +502,30 @@ private JWSSigner createSigner(final AssertionConfig.AssertionKey assertionKey) | |
| private JWSVerifier createVerifier(AssertionConfig.AssertionKey assertionKey) throws JOSEException { | ||
| switch (assertionKey.alg) { | ||
| case RS256: | ||
| return new RSASSAVerifier((RSAPublicKey) assertionKey.key); | ||
| if (assertionKey.key instanceof JWK) { | ||
| return createVerifier((JWK) assertionKey.key); | ||
| } else if (assertionKey.key instanceof RSAPublicKey) { | ||
| return createVerifier((RSAPublicKey) assertionKey.key); | ||
| } else { | ||
| throw new SDKException("Expected JWK or RSAPublicKey for RS256 algorithm"); | ||
| } | ||
| case HS256: | ||
| return new MACVerifier((byte[]) assertionKey.key); | ||
| default: | ||
| throw new SDKException("Unknown verify key, unable to verify assertion signature"); | ||
| } | ||
| } | ||
|
|
||
| private JWSVerifier createVerifier(JWK jwk) throws JOSEException { | ||
| if (jwk instanceof com.nimbusds.jose.jwk.RSAKey) { | ||
| return new RSASSAVerifier(jwk.toRSAKey()); | ||
| } | ||
| throw new JOSEException("Unsupported JWK type: " + jwk.getKeyType() + ". Only RSA keys are supported."); | ||
| } | ||
|
|
||
| private JWSVerifier createVerifier(RSAPublicKey publicKey) { | ||
| return new RSASSAVerifier(publicKey); | ||
| } | ||
| } | ||
|
|
||
| public static class AssertionValueAdapter implements JsonDeserializer<AssertionConfig.Statement> { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good