mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Make sd-jwt key binding verification work with EdDSA keys
closes #44369 Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
parent
d0e4d1f620
commit
cbb823bc0e
@ -24,4 +24,8 @@ public class CryptoConstants {
|
||||
/** Name of Java security provider used with fips BouncyCastle. Should be used in FIPS environment */
|
||||
public static final String BCFIPS_PROVIDER_ID = "BCFIPS";
|
||||
|
||||
public static final String EC_KEY_SECP256R1 = "secp256r1";
|
||||
public static final String EC_KEY_SECP384R1 = "secp384r1";
|
||||
public static final String EC_KEY_SECP521R1 = "secp521r1";
|
||||
|
||||
}
|
||||
|
||||
@ -25,7 +25,9 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@ -72,6 +74,27 @@ public class KeyUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyPair generateEddsaKeyPair(String curveName) {
|
||||
try {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(curveName);
|
||||
return keyGen.generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyPair generateEcKeyPair(String keySpecName) {
|
||||
try {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
|
||||
SecureRandom randomGen = new SecureRandom();
|
||||
ECGenParameterSpec ecSpec = new ECGenParameterSpec(keySpecName);
|
||||
keyGen.initialize(ecSpec, randomGen);
|
||||
return keyGen.generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String createKeyId(Key key) {
|
||||
try {
|
||||
return Base64Url.encode(MessageDigest.getInstance(DEFAULT_MESSAGE_DIGEST).digest(key.getEncoded()));
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.keycloak.crypto;
|
||||
|
||||
public enum ECCurve {
|
||||
P256,
|
||||
P384,
|
||||
P521;
|
||||
|
||||
/**
|
||||
* Convert standard EC curve names (and aliases) into this enum.
|
||||
*/
|
||||
public static ECCurve fromStdCrv(String crv) {
|
||||
switch (crv) {
|
||||
case "P-256":
|
||||
case "secp256r1":
|
||||
return P256;
|
||||
case "P-384":
|
||||
case "secp384r1":
|
||||
return P384;
|
||||
case "P-521":
|
||||
case "secp521r1":
|
||||
return P521;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected EC curve: " + crv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP256R1;
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP384R1;
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP521R1;
|
||||
|
||||
public class KeyWrapper {
|
||||
|
||||
private String providerId;
|
||||
@ -82,6 +86,8 @@ public class KeyWrapper {
|
||||
* <p>For keys of type {@link KeyType#EC}, {@link Algorithm#ES256}, {@link Algorithm#ES384}, or {@link Algorithm#ES512}
|
||||
* is returned based on the curve
|
||||
*
|
||||
* <p>For keys of type {@link KeyType#OKP}, {@link Algorithm#EdDSA} as that is the only value supported for that key type
|
||||
*
|
||||
* @return the algorithm set or a default based on the key type.
|
||||
*/
|
||||
public String getAlgorithmOrDefault() {
|
||||
@ -91,15 +97,21 @@ public class KeyWrapper {
|
||||
if (curve != null) {
|
||||
switch (curve) {
|
||||
case "P-256":
|
||||
case EC_KEY_SECP256R1:
|
||||
return Algorithm.ES256;
|
||||
case "P-384":
|
||||
case EC_KEY_SECP384R1:
|
||||
return Algorithm.ES384;
|
||||
case "P-512":
|
||||
case "P-521":
|
||||
case EC_KEY_SECP521R1:
|
||||
return Algorithm.ES512;
|
||||
}
|
||||
}
|
||||
case KeyType.RSA:
|
||||
return Algorithm.RS256;
|
||||
case KeyType.OKP:
|
||||
return Algorithm.EdDSA;
|
||||
}
|
||||
}
|
||||
return algorithm;
|
||||
|
||||
@ -34,7 +34,6 @@ import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.crypto.SignatureSignerContext;
|
||||
import org.keycloak.jose.jwk.JWK;
|
||||
import org.keycloak.jose.jws.JWSHeader;
|
||||
import org.keycloak.sdjwt.vp.KeyBindingJWT;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
@ -44,6 +43,7 @@ import com.fasterxml.jackson.databind.node.LongNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_CNF;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_JWK;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_SD;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_SD_HASH_ALGORITHM;
|
||||
|
||||
@ -461,27 +461,10 @@ public class IssuerSignedJWT extends JwsToken {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* this method requires the public key to be present in the keybindingJwts header as "jwk" claim
|
||||
*/
|
||||
public Builder withKeyBinding(KeyBindingJWT keyBinding) {
|
||||
public Builder withKeyBindingKey(JWK keyBinding) {
|
||||
ObjectNode jwkNode = JsonSerialization.mapper.convertValue(keyBinding, ObjectNode.class);
|
||||
ObjectNode cnf = JsonNodeFactory.instance.objectNode();
|
||||
Optional.ofNullable(keyBinding.getJwsHeader().getOtherClaims().get(OID4VCConstants.CLAIM_NAME_JWK))
|
||||
.map(map -> JsonSerialization.mapper.convertValue(map, ObjectNode.class))
|
||||
.ifPresent(jwkNode -> cnf.set(OID4VCConstants.CLAIM_NAME_JWK, jwkNode));
|
||||
if (!cnf.isEmpty()) {
|
||||
getClaims().add(new VisibleSdJwtClaim(SdJwtClaimName.of(CLAIM_NAME_CNF), cnf));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withKeyBinding(JWK keyBinding) {
|
||||
return withKeyBinding(JsonSerialization.mapper.convertValue(keyBinding, ObjectNode.class));
|
||||
}
|
||||
|
||||
public Builder withKeyBinding(ObjectNode keyBinding) {
|
||||
ObjectNode cnf = JsonNodeFactory.instance.objectNode();
|
||||
cnf.set("jwk", keyBinding);
|
||||
cnf.set(CLAIM_NAME_JWK, jwkNode);
|
||||
getClaims().add(new VisibleSdJwtClaim(SdJwtClaimName.of(CLAIM_NAME_CNF), cnf));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -19,15 +19,11 @@ package org.keycloak.sdjwt;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.AsymmetricSignatureVerifierContext;
|
||||
import org.keycloak.crypto.ECCurve;
|
||||
import org.keycloak.crypto.ECDSASignatureVerifierContext;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyWrapper;
|
||||
import org.keycloak.crypto.SignatureVerifierContext;
|
||||
import org.keycloak.jose.jwk.JWK;
|
||||
import org.keycloak.util.JWKSUtils;
|
||||
import org.keycloak.util.KeyWrapperUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
@ -50,7 +46,6 @@ public class JwkParsingUtils {
|
||||
|
||||
public static SignatureVerifierContext convertJwkToVerifierContext(JWK jwk) {
|
||||
// Wrap JWK
|
||||
|
||||
KeyWrapper keyWrapper;
|
||||
|
||||
try {
|
||||
@ -61,39 +56,6 @@ public class JwkParsingUtils {
|
||||
}
|
||||
|
||||
// Build verifier
|
||||
|
||||
// KeyType.EC
|
||||
if (keyWrapper.getType().equals(KeyType.EC)) {
|
||||
if (keyWrapper.getAlgorithm() == null) {
|
||||
Objects.requireNonNull(keyWrapper.getCurve());
|
||||
|
||||
String alg = null;
|
||||
switch (ECCurve.fromStdCrv(keyWrapper.getCurve())) {
|
||||
case P256:
|
||||
alg = Algorithm.ES256;
|
||||
break;
|
||||
case P384:
|
||||
alg = Algorithm.ES384;
|
||||
break;
|
||||
case P521:
|
||||
alg = Algorithm.ES512;
|
||||
break;
|
||||
}
|
||||
|
||||
keyWrapper.setAlgorithm(alg);
|
||||
}
|
||||
|
||||
return new ECDSASignatureVerifierContext(keyWrapper);
|
||||
}
|
||||
|
||||
// KeyType.RSA
|
||||
if (keyWrapper.getType().equals(KeyType.RSA)) {
|
||||
return new AsymmetricSignatureVerifierContext(keyWrapper);
|
||||
}
|
||||
|
||||
// KeyType is not supported
|
||||
// This is unreachable as of now given that `JWKSUtils.getKeyWrapper` will fail
|
||||
// on JWKs with key type not equal to EC or RSA.
|
||||
throw new IllegalArgumentException("Unexpected key type: " + keyWrapper.getType());
|
||||
return KeyWrapperUtil.createSignatureVerifierContext(keyWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,9 +24,6 @@ import java.security.PrivateKey;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.crypto.AsymmetricSignatureSignerContext;
|
||||
import org.keycloak.crypto.ECDSASignatureSignerContext;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
import org.keycloak.crypto.KeyWrapper;
|
||||
import org.keycloak.crypto.SignatureSignerContext;
|
||||
@ -93,23 +90,11 @@ public class DPoPGenerator {
|
||||
}
|
||||
|
||||
private String sign(JWSHeader jwsHeader, DPoP dpop, KeyWrapper keyWrapper) {
|
||||
SignatureSignerContext sigCtx = createSignatureSignerContext(keyWrapper);
|
||||
SignatureSignerContext sigCtx = KeyWrapperUtil.createSignatureSignerContext(keyWrapper);
|
||||
|
||||
return new JWSBuilder()
|
||||
.header(jwsHeader)
|
||||
.jsonContent(dpop)
|
||||
.sign(sigCtx);
|
||||
}
|
||||
|
||||
private SignatureSignerContext createSignatureSignerContext(KeyWrapper keyWrapper) {
|
||||
switch (keyWrapper.getType()) {
|
||||
case KeyType.EC:
|
||||
return new ECDSASignatureSignerContext(keyWrapper);
|
||||
case KeyType.RSA:
|
||||
case KeyType.OKP:
|
||||
return new AsymmetricSignatureSignerContext(keyWrapper);
|
||||
default:
|
||||
throw new IllegalArgumentException("No signer provider for key algorithm type " + keyWrapper.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
core/src/main/java/org/keycloak/util/KeyWrapperUtil.java
Normal file
40
core/src/main/java/org/keycloak/util/KeyWrapperUtil.java
Normal file
@ -0,0 +1,40 @@
|
||||
package org.keycloak.util;
|
||||
|
||||
import org.keycloak.crypto.AsymmetricSignatureSignerContext;
|
||||
import org.keycloak.crypto.AsymmetricSignatureVerifierContext;
|
||||
import org.keycloak.crypto.ECDSASignatureSignerContext;
|
||||
import org.keycloak.crypto.ECDSASignatureVerifierContext;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyWrapper;
|
||||
import org.keycloak.crypto.SignatureSignerContext;
|
||||
import org.keycloak.crypto.SignatureVerifierContext;
|
||||
|
||||
public class KeyWrapperUtil {
|
||||
|
||||
public static SignatureSignerContext createSignatureSignerContext(KeyWrapper keyWrapper) {
|
||||
switch (keyWrapper.getType()) {
|
||||
case KeyType.EC:
|
||||
return new ECDSASignatureSignerContext(keyWrapper);
|
||||
case KeyType.RSA:
|
||||
case KeyType.OKP:
|
||||
return new AsymmetricSignatureSignerContext(keyWrapper);
|
||||
default:
|
||||
throw new IllegalArgumentException("No signer provider for key algorithm type " + keyWrapper.getType());
|
||||
}
|
||||
}
|
||||
|
||||
public static SignatureVerifierContext createSignatureVerifierContext(KeyWrapper keyWrapper) {
|
||||
switch (keyWrapper.getType()) {
|
||||
case KeyType.EC:
|
||||
return new ECDSASignatureVerifierContext(keyWrapper);
|
||||
case KeyType.RSA:
|
||||
case KeyType.OKP:
|
||||
return new AsymmetricSignatureVerifierContext(keyWrapper);
|
||||
default:
|
||||
throw new IllegalArgumentException("No signer provider for key algorithm type " + keyWrapper.getType());
|
||||
}
|
||||
}
|
||||
|
||||
private KeyWrapperUtil() {
|
||||
}
|
||||
}
|
||||
@ -20,9 +20,6 @@ package org.keycloak.sdjwt;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
@ -55,6 +52,8 @@ import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP256R1;
|
||||
|
||||
/**
|
||||
* @author Pascal Knueppel
|
||||
* @since 13.11.2025
|
||||
@ -185,10 +184,10 @@ public abstract class SdJwtCreationAndSigningTest {
|
||||
public void testCreateSdJwtWithKeybindingJwt() throws Exception {
|
||||
final String authorizationServerUrl = "https://example.com";
|
||||
|
||||
KeyWrapper issuerKeyPair = toKeyWrapper(createEcKey());
|
||||
KeyWrapper issuerKeyPair = toKeyWrapper(KeyUtils.generateEcKeyPair(EC_KEY_SECP256R1));
|
||||
JWK issuerJwk = JWKBuilder.create().ec(issuerKeyPair.getPublicKey());
|
||||
|
||||
KeyWrapper holderKeyPair = toKeyWrapper(createEcKey());
|
||||
KeyWrapper holderKeyPair = toKeyWrapper(KeyUtils.generateEcKeyPair(EC_KEY_SECP256R1));
|
||||
JWK holderKeybindingKey = JWKBuilder.create().ec(holderKeyPair.getPublicKey());
|
||||
|
||||
SignatureSignerContext issuerSignerContext = new ECDSASignatureSignerContext(issuerKeyPair);
|
||||
@ -225,7 +224,7 @@ public abstract class SdJwtCreationAndSigningTest {
|
||||
.withKid(issuerJwk.getKeyId())
|
||||
/* body */
|
||||
.withClaims(disclosures, disclosureSpec)
|
||||
.withKeyBinding(holderKeybindingKey)
|
||||
.withKeyBindingKey(holderKeybindingKey)
|
||||
.withIat(iat)
|
||||
.withNbf(nbf)
|
||||
.withExp(exp)
|
||||
@ -384,16 +383,6 @@ public abstract class SdJwtCreationAndSigningTest {
|
||||
}
|
||||
}
|
||||
|
||||
public KeyPair createEcKey() {
|
||||
try {
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
|
||||
kpg.initialize(new ECGenParameterSpec("secp521r1"), new SecureRandom());
|
||||
return kpg.generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public KeyWrapper toKeyWrapper(KeyPair keyPair) {
|
||||
KeyWrapper keyWrapper = new KeyWrapper();
|
||||
keyWrapper.setKid(KeyUtils.createKeyId(keyPair.getPublic()));
|
||||
|
||||
@ -19,10 +19,8 @@ package org.keycloak.sdjwt;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPrivateKeySpec;
|
||||
@ -193,10 +191,7 @@ public class TestSettings {
|
||||
// generate key spec
|
||||
private static ECParameterSpec generateEcdsaKeySpec(String paramSpecName) {
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
|
||||
ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(paramSpecName);
|
||||
keyPairGenerator.initialize(ecGenParameterSpec);
|
||||
KeyPair keyPair = keyPairGenerator.generateKeyPair();
|
||||
KeyPair keyPair = KeyUtils.generateEcKeyPair(paramSpecName);
|
||||
return ((java.security.interfaces.ECPublicKey) keyPair.getPublic()).getParams();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error obtaining ECParameterSpec for P-256 curve", e);
|
||||
|
||||
@ -0,0 +1,242 @@
|
||||
package org.keycloak.sdjwt.sdjwtvp;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.KeyWrapper;
|
||||
import org.keycloak.crypto.SignatureVerifierContext;
|
||||
import org.keycloak.jose.jwk.ECPublicJWK;
|
||||
import org.keycloak.jose.jwk.JWK;
|
||||
import org.keycloak.jose.jwk.JWKBuilder;
|
||||
import org.keycloak.jose.jwk.OKPPublicJWK;
|
||||
import org.keycloak.jose.jwk.RSAPublicJWK;
|
||||
import org.keycloak.rule.CryptoInitRule;
|
||||
import org.keycloak.sdjwt.DisclosureSpec;
|
||||
import org.keycloak.sdjwt.IssuerSignedJWT;
|
||||
import org.keycloak.sdjwt.IssuerSignedJwtVerificationOpts;
|
||||
import org.keycloak.sdjwt.SdJwt;
|
||||
import org.keycloak.sdjwt.SdJwtUtils;
|
||||
import org.keycloak.sdjwt.TestSettings;
|
||||
import org.keycloak.sdjwt.TestUtils;
|
||||
import org.keycloak.sdjwt.vp.KeyBindingJWT;
|
||||
import org.keycloak.sdjwt.vp.KeyBindingJwtVerificationOpts;
|
||||
import org.keycloak.sdjwt.vp.SdJwtVP;
|
||||
import org.keycloak.util.JWKSUtils;
|
||||
import org.keycloak.util.KeyWrapperUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_EXP;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_IAT;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_ISSUER;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_JWK;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_NBF;
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP256R1;
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP384R1;
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP521R1;
|
||||
import static org.keycloak.sdjwt.sdjwtvp.SdJwtVPVerificationTest.testSettings;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
/**
|
||||
* Test of various algorithms and scenarios for SD-JWT key binding
|
||||
*/
|
||||
public abstract class SdJwtKeyBindingTest {
|
||||
|
||||
@ClassRule
|
||||
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
|
||||
|
||||
@Test
|
||||
public void testEdDSAKeyBindingWithEd25519() throws VerificationException {
|
||||
testKeyBinding(() -> KeyUtils.generateEddsaKeyPair(Algorithm.Ed25519),
|
||||
keyPair -> JWKBuilder.create().okp(keyPair.getPublic()),
|
||||
jwk -> assertEdDSAKey(jwk, Algorithm.Ed25519));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEdDSAKeyBindingWithEd448() throws VerificationException {
|
||||
testKeyBinding(() -> KeyUtils.generateEddsaKeyPair(Algorithm.Ed448),
|
||||
keyPair -> JWKBuilder.create().okp(keyPair.getPublic()),
|
||||
jwk -> assertEdDSAKey(jwk, Algorithm.Ed448));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEc256KeyBinding() throws VerificationException {
|
||||
testKeyBinding(() -> KeyUtils.generateEcKeyPair(EC_KEY_SECP256R1),
|
||||
keyPair -> JWKBuilder.create().ec(keyPair.getPublic()),
|
||||
jwk -> assertEcKey(jwk, "P-256"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEc384KeyBinding() throws VerificationException {
|
||||
testKeyBinding(() -> KeyUtils.generateEcKeyPair(EC_KEY_SECP384R1),
|
||||
keyPair -> JWKBuilder.create().ec(keyPair.getPublic()),
|
||||
jwk -> assertEcKey(jwk, "P-384"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEc521KeyBinding() throws VerificationException {
|
||||
testKeyBinding(() -> KeyUtils.generateEcKeyPair(EC_KEY_SECP521R1),
|
||||
keyPair -> JWKBuilder.create().ec(keyPair.getPublic()),
|
||||
jwk -> assertEcKey(jwk, "P-521"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRSA2048KeyBinding() throws VerificationException {
|
||||
testKeyBinding(() -> KeyUtils.generateRsaKeyPair(2048),
|
||||
keyPair -> JWKBuilder.create().rsa(keyPair.getPublic()),
|
||||
jwk -> assertRsaKey(jwk));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRSA4096KeyBinding() throws VerificationException {
|
||||
testKeyBinding(() -> KeyUtils.generateRsaKeyPair(4096),
|
||||
keyPair -> JWKBuilder.create().rsa(keyPair.getPublic()),
|
||||
jwk -> assertRsaKey(jwk));
|
||||
}
|
||||
|
||||
private void testKeyBinding(Supplier<KeyPair> keyPairSupplier, Function<KeyPair, JWK> jwkProvider, Consumer<JWK> keyFormatValidator) throws VerificationException {
|
||||
DisclosureSpec disclosureSpec = DisclosureSpec.builder()
|
||||
.withUndisclosedClaim("given_name", "2GLC42sKQveCfGfryNRN9w")
|
||||
.withUndisclosedClaim("family_name", "eluV5Og3gSNII8EYnsxA_A")
|
||||
.withUndisclosedClaim("email", "6Ij7tM-a5iVPGboS5tmvVA")
|
||||
.build();
|
||||
|
||||
// Read claims provided by the holder
|
||||
ObjectNode holderClaimSet = TestUtils.readClaimSet(getClass(), "sdjwt/s3.3-holder-claims.json");
|
||||
|
||||
int currentTime = Time.currentTime();
|
||||
holderClaimSet.put(CLAIM_NAME_ISSUER, "https://example.com/issuer");
|
||||
holderClaimSet.put(CLAIM_NAME_IAT, currentTime);
|
||||
holderClaimSet.put(CLAIM_NAME_NBF, currentTime);
|
||||
holderClaimSet.put(CLAIM_NAME_EXP, currentTime + 60);
|
||||
|
||||
// Generate key-binding key pair
|
||||
KeyPair keyPair = keyPairSupplier.get();
|
||||
JWK publicJwk = jwkProvider.apply(keyPair);
|
||||
|
||||
KeyWrapper keyWrapper = JWKSUtils.getKeyWrapper(publicJwk);
|
||||
keyWrapper.setPrivateKey(keyPair.getPrivate());
|
||||
|
||||
KeyBindingJWT keyBindingJWT = generateKeyBindingJWT(publicJwk.getKeyId());
|
||||
|
||||
// Create issuer-signed JWT with the key attached
|
||||
IssuerSignedJWT issuerSignedJWT = IssuerSignedJWT.builder()
|
||||
.withClaims(holderClaimSet, disclosureSpec)
|
||||
.withKeyBindingKey(publicJwk)
|
||||
.build();
|
||||
SdJwt sdJwt = SdJwt.builder()
|
||||
.withIssuerSignedJwt(issuerSignedJWT)
|
||||
.withKeybindingJwt(keyBindingJWT)
|
||||
.build(TestSettings.getInstance().getIssuerSignerContext(), KeyWrapperUtil.createSignatureSignerContext(keyWrapper));
|
||||
|
||||
String sdJwtString = sdJwt.toString();
|
||||
|
||||
// 2 - Parse presentation and verify successfully (especially key binding)
|
||||
|
||||
// Just use the presentation with all the claims disclosed as provided by issuer
|
||||
SdJwtVP sdJwtVP = SdJwtVP.of(sdJwtString);
|
||||
|
||||
// Expect correct JWK
|
||||
JsonNode cnf = sdJwtVP.getCnfClaim();
|
||||
Assert.assertNotNull(cnf);
|
||||
JWK jwk = SdJwtUtils.mapper.convertValue(cnf.get(CLAIM_NAME_JWK), JWK.class);
|
||||
|
||||
keyFormatValidator.accept(jwk);
|
||||
|
||||
sdJwtVP.verify(
|
||||
defaultIssuerVerifyingKeys(),
|
||||
defaultIssuerSignedJwtVerificationOpts().build(),
|
||||
defaultKeyBindingJwtVerificationOpts().build()
|
||||
);
|
||||
|
||||
// 3 - Test incorrect key-binding signature
|
||||
KeyBindingJWT invalidBindingJWT = generateKeyBindingJWT(publicJwk.getKeyId());
|
||||
invalidBindingJWT.getPayload().put("nonce", "invalid");
|
||||
String invalidSdJwt = SdJwt.builder()
|
||||
.withIssuerSignedJwt(issuerSignedJWT)
|
||||
.withKeybindingJwt(invalidBindingJWT)
|
||||
.build(TestSettings.getInstance().getIssuerSignerContext(), KeyWrapperUtil.createSignatureSignerContext(keyWrapper))
|
||||
.toString();
|
||||
|
||||
// Replace signature with the signature from valid sdJwt
|
||||
String signature1 = sdJwtString.substring(sdJwtString.lastIndexOf('.') + 1);
|
||||
invalidSdJwt = invalidSdJwt.substring(0, invalidSdJwt.lastIndexOf('.') + 1);
|
||||
invalidSdJwt = invalidSdJwt + signature1;
|
||||
|
||||
SdJwtVP invalidSdJwtVP = SdJwtVP.of(invalidSdJwt);
|
||||
try {
|
||||
invalidSdJwtVP.verify(
|
||||
defaultIssuerVerifyingKeys(),
|
||||
defaultIssuerSignedJwtVerificationOpts().build(),
|
||||
defaultKeyBindingJwtVerificationOpts().build()
|
||||
);
|
||||
Assert.fail("Not expected to successfully validate key-binding JWT");
|
||||
} catch (VerificationException ve) {
|
||||
Assert.assertEquals("Key binding JWT invalid", ve.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertEdDSAKey(JWK jwk, String expectedCurve) {
|
||||
Assert.assertEquals(2, jwk.getOtherClaims().size());
|
||||
Assert.assertEquals(expectedCurve, jwk.getOtherClaims().get(OKPPublicJWK.CRV));
|
||||
Assert.assertThat(jwk.getOtherClaims().containsKey(OKPPublicJWK.X), is(true));
|
||||
}
|
||||
|
||||
private void assertEcKey(JWK jwk, String expectedCurve) {
|
||||
Assert.assertEquals(3, jwk.getOtherClaims().size());
|
||||
Assert.assertEquals(expectedCurve, jwk.getOtherClaims().get(ECPublicJWK.CRV));
|
||||
Assert.assertThat(jwk.getOtherClaims().containsKey(ECPublicJWK.X), is(true));
|
||||
Assert.assertThat(jwk.getOtherClaims().containsKey(ECPublicJWK.Y), is(true));
|
||||
}
|
||||
|
||||
private void assertRsaKey(JWK jwk) {
|
||||
Assert.assertEquals(2, jwk.getOtherClaims().size());
|
||||
Assert.assertThat(jwk.getOtherClaims().containsKey(RSAPublicJWK.PUBLIC_EXPONENT), is(true));
|
||||
Assert.assertThat(jwk.getOtherClaims().containsKey(RSAPublicJWK.MODULUS), is(true));
|
||||
}
|
||||
|
||||
private KeyBindingJWT generateKeyBindingJWT(String keyId) {
|
||||
int currentTime = Time.currentTime();
|
||||
|
||||
return KeyBindingJWT.builder()
|
||||
/* header */
|
||||
.withKid(keyId)
|
||||
/* body */
|
||||
.withIat(currentTime)
|
||||
.withNbf(currentTime)
|
||||
.withExp(currentTime + 60)
|
||||
.withNonce("1234567890")
|
||||
.withAudience("https://verifier.example.org")
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private List<SignatureVerifierContext> defaultIssuerVerifyingKeys() {
|
||||
return Collections.singletonList(testSettings.issuerVerifierContext);
|
||||
}
|
||||
|
||||
private IssuerSignedJwtVerificationOpts.Builder defaultIssuerSignedJwtVerificationOpts() {
|
||||
return IssuerSignedJwtVerificationOpts.builder()
|
||||
.withClockSkew(0);
|
||||
}
|
||||
|
||||
private KeyBindingJwtVerificationOpts.Builder defaultKeyBindingJwtVerificationOpts() {
|
||||
return KeyBindingJwtVerificationOpts.builder()
|
||||
.withKeyBindingRequired(true)
|
||||
.withNonceCheck("1234567890")
|
||||
.withAudCheck("https://verifier.example.org");
|
||||
}
|
||||
}
|
||||
@ -38,6 +38,7 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:francis.pouatcha@adorsys.com">Francis Pouatcha</a>
|
||||
*/
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
package org.keycloak.crypto.def.test.sdjwt;
|
||||
|
||||
import org.keycloak.common.util.Environment;
|
||||
import org.keycloak.sdjwt.sdjwtvp.SdJwtKeyBindingTest;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
|
||||
public class DefaultCryptoSdJwtKeyBindingTest extends SdJwtKeyBindingTest {
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
// Run this test just if java is not in FIPS mode
|
||||
Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package org.keycloak.crypto.elytron.test.sdjwt;
|
||||
|
||||
import org.keycloak.sdjwt.sdjwtvp.SdJwtKeyBindingTest;
|
||||
|
||||
public class ElytronCryptoSdJwtKeyBindingTest extends SdJwtKeyBindingTest {
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package org.keycloak.crypto.fips.test.sdjwt;
|
||||
|
||||
import org.keycloak.common.util.Environment;
|
||||
import org.keycloak.sdjwt.sdjwtvp.SdJwtKeyBindingTest;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
|
||||
public class FIPS1402SdJwtKeyBindingTest extends SdJwtKeyBindingTest {
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
// Run this test just if java is not in FIPS mode
|
||||
Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode());
|
||||
}
|
||||
}
|
||||
@ -16,10 +16,6 @@
|
||||
*/
|
||||
package org.keycloak.keys;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
@ -49,18 +45,6 @@ public abstract class AbstractEcKeyProviderFactory<T extends KeyProvider> implem
|
||||
.checkBoolean(Attributes.EC_GENERATE_CERTIFICATE_PROPERTY, false);
|
||||
}
|
||||
|
||||
public static KeyPair generateEcKeyPair(String keySpecName) {
|
||||
try {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
|
||||
SecureRandom randomGen = new SecureRandom();
|
||||
ECGenParameterSpec ecSpec = new ECGenParameterSpec(keySpecName);
|
||||
keyGen.initialize(ecSpec, randomGen);
|
||||
return keyGen.generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertECDomainParmNistRepToSecRep(String ecInNistRep) {
|
||||
// convert Elliptic Curve Domain Parameter Name in NIST to SEC which is used to generate its EC key
|
||||
String ecInSecRep = null;
|
||||
|
||||
@ -16,9 +16,6 @@
|
||||
*/
|
||||
package org.keycloak.keys;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
@ -59,13 +56,4 @@ public abstract class AbstractEddsaKeyProviderFactory implements KeyProviderFact
|
||||
.checkBoolean(Attributes.ACTIVE_PROPERTY, false);
|
||||
}
|
||||
|
||||
public static KeyPair generateEddsaKeyPair(String curveName) {
|
||||
try {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(curveName);
|
||||
return keyGen.generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
@ -102,7 +103,7 @@ public abstract class AbstractGeneratedEcKeyProviderFactory<T extends KeyProvide
|
||||
protected void generateKeys(ComponentModel model, String ecInNistRep) {
|
||||
KeyPair keyPair;
|
||||
try {
|
||||
keyPair = generateEcKeyPair(convertECDomainParmNistRepToSecRep(ecInNistRep));
|
||||
keyPair = KeyUtils.generateEcKeyPair(convertECDomainParmNistRepToSecRep(ecInNistRep));
|
||||
model.put(getEcPrivateKeyKey(), Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()));
|
||||
model.put(getEcPublicKeyKey(), Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
|
||||
model.put(getEcEllipticCurveKey(), ecInNistRep);
|
||||
|
||||
@ -23,6 +23,7 @@ import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
@ -120,7 +121,7 @@ public class GeneratedEddsaKeyProviderFactory extends AbstractEddsaKeyProviderFa
|
||||
private void generateKeys(ComponentModel model, String curveName) {
|
||||
KeyPair keyPair;
|
||||
try {
|
||||
keyPair = generateEddsaKeyPair(curveName);
|
||||
keyPair = KeyUtils.generateEddsaKeyPair(curveName);
|
||||
model.put(EDDSA_PRIVATE_KEY_KEY, Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()));
|
||||
model.put(EDDSA_PUBLIC_KEY_KEY, Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
|
||||
model.put(EDDSA_ELLIPTIC_CURVE_KEY, curveName);
|
||||
|
||||
@ -19,7 +19,6 @@ package org.keycloak.jose.jwk;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
@ -51,8 +50,7 @@ public class ServerJWKTest {
|
||||
|
||||
@Test
|
||||
public void publicEd25519() throws Exception {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(Algorithm.Ed25519);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
KeyPair keyPair = KeyUtils.generateEddsaKeyPair(Algorithm.Ed25519);
|
||||
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
JWK jwk = JWKBuilder.create().kid(KeyUtils.createKeyId(keyPair.getPublic())).algorithm(Algorithm.EdDSA).okp(publicKey);
|
||||
@ -82,8 +80,7 @@ public class ServerJWKTest {
|
||||
|
||||
@Test
|
||||
public void publicEd448() throws Exception {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(Algorithm.Ed448);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
KeyPair keyPair = KeyUtils.generateEddsaKeyPair(Algorithm.Ed448);
|
||||
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
JWK jwk = JWKBuilder.create().kid(KeyUtils.createKeyId(keyPair.getPublic())).algorithm(Algorithm.EdDSA).okp(publicKey);
|
||||
|
||||
@ -4,12 +4,11 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.ECDSASignatureSignerContext;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
@ -26,6 +25,8 @@ import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP256R1;
|
||||
|
||||
public class OAuthIdentityProvider {
|
||||
|
||||
private final HttpServer httpServer;
|
||||
@ -123,10 +124,7 @@ public class OAuthIdentityProvider {
|
||||
|
||||
KeyUse keyUse = spiffe ? KeyUse.JWT_SVID : KeyUse.SIG;
|
||||
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
|
||||
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
|
||||
keyPairGenerator.initialize(ecSpec);
|
||||
KeyPair keyPair = keyPairGenerator.generateKeyPair();
|
||||
KeyPair keyPair = KeyUtils.generateEcKeyPair(EC_KEY_SECP256R1);
|
||||
|
||||
JWK jwk = JWKBuilder.create().ec(keyPair.getPublic());
|
||||
if (!spiffe) {
|
||||
|
||||
@ -19,13 +19,8 @@ package org.keycloak.testsuite.rest.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
@ -90,6 +85,10 @@ import org.keycloak.util.JsonSerialization;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.jboss.resteasy.reactive.NoCache;
|
||||
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP256R1;
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP384R1;
|
||||
import static org.keycloak.common.crypto.CryptoConstants.EC_KEY_SECP521R1;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@ -139,22 +138,22 @@ public class TestingOIDCEndpointsApplicationResource {
|
||||
break;
|
||||
case Algorithm.ES256:
|
||||
keyType = KeyType.EC;
|
||||
keyPair = generateEcdsaKey("secp256r1");
|
||||
keyPair = KeyUtils.generateEcKeyPair(EC_KEY_SECP256R1);
|
||||
break;
|
||||
case Algorithm.ES384:
|
||||
keyType = KeyType.EC;
|
||||
keyPair = generateEcdsaKey("secp384r1");
|
||||
keyPair = KeyUtils.generateEcKeyPair(EC_KEY_SECP384R1);
|
||||
break;
|
||||
case Algorithm.ES512:
|
||||
keyType = KeyType.EC;
|
||||
keyPair = generateEcdsaKey("secp521r1");
|
||||
keyPair = KeyUtils.generateEcKeyPair(EC_KEY_SECP521R1);
|
||||
break;
|
||||
case Algorithm.EdDSA:
|
||||
if (curve == null) {
|
||||
curve = Algorithm.Ed25519;
|
||||
}
|
||||
keyType = KeyType.OKP;
|
||||
keyPair = generateEddsaKey(curve);
|
||||
keyPair = KeyUtils.generateEddsaKeyPair(curve);
|
||||
break;
|
||||
case JWEConstants.RSA1_5:
|
||||
case JWEConstants.RSA_OAEP:
|
||||
@ -186,21 +185,6 @@ public class TestingOIDCEndpointsApplicationResource {
|
||||
return getKeysAsPem();
|
||||
}
|
||||
|
||||
private KeyPair generateEcdsaKey(String ecDomainParamName) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
|
||||
SecureRandom randomGen = new SecureRandom();
|
||||
ECGenParameterSpec ecSpec = new ECGenParameterSpec(ecDomainParamName);
|
||||
keyGen.initialize(ecSpec, randomGen);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
private KeyPair generateEddsaKey(String curveName) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(curveName);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("/get-keys-as-pem")
|
||||
|
||||
@ -357,7 +357,7 @@ public class DPoPTest extends AbstractTestRealmKeycloakTest {
|
||||
public void testDPoPProofByConfidentialClient_EdDSA() throws Exception {
|
||||
// Generating keys
|
||||
String curveName = AbstractEddsaKeyProviderFactory.DEFAULT_EDDSA_ELLIPTIC_CURVE;
|
||||
KeyPair keyPair = AbstractEddsaKeyProviderFactory.generateEddsaKeyPair(curveName);
|
||||
KeyPair keyPair = KeyUtils.generateEddsaKeyPair(curveName);
|
||||
|
||||
// JWK
|
||||
JWKBuilder b = JWKBuilder.create()
|
||||
|
||||
@ -21,12 +21,9 @@ import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@ -468,12 +465,7 @@ public final class ClientPoliciesUtil {
|
||||
}
|
||||
|
||||
public static KeyPair generateEcdsaKey(String ecDomainParamName) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
|
||||
SecureRandom randomGen = new SecureRandom();
|
||||
ECGenParameterSpec ecSpec = new ECGenParameterSpec(ecDomainParamName);
|
||||
keyGen.initialize(ecSpec, randomGen);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
return keyPair;
|
||||
return org.keycloak.common.util.KeyUtils.generateEcKeyPair(ecDomainParamName);
|
||||
}
|
||||
|
||||
public static String generateSignedDPoPProof(String jti, String htm, String htu, Long iat, String algorithm, JWSHeader jwsHeader, PrivateKey privateKey, String accessToken) throws IOException {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user