Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
d4220d51d4fae91532e066b2042f092ab585e823d5b8985092ce0151edb6d2e249a3f8b386c1f5c974324d3dd13092fe1233f9d5b1c36aa06a3a3f9File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
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 | ||
pflynn-virtru marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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); | ||
| } | ||
cshamrick marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
pflynn-virtru marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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."); | ||
| } | ||
cshamrick marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. cshamrick marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| private JWSVerifier createVerifier(RSAPublicKey publicKey) { | ||
| return new RSASSAVerifier(publicKey); | ||
| } | ||
| } | ||
| public static class AssertionValueAdapter implements JsonDeserializer<AssertionConfig.Statement> { | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.