mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Avoid MRJAR in keycloak-core
Closes #34630 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
ca1c10f7ba
commit
c1d4dad4dc
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -88,7 +88,7 @@ jobs:
|
||||
SEP=","
|
||||
done
|
||||
|
||||
./mvnw install -pl "$PROJECTS" -am
|
||||
./mvnw test -pl "$PROJECTS" -am
|
||||
|
||||
- name: Upload JVM Heapdumps
|
||||
if: always()
|
||||
|
||||
18
core/pom.xml
18
core/pom.xml
@ -77,9 +77,9 @@
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>jdk-16</id>
|
||||
<id>jdk-15</id>
|
||||
<activation>
|
||||
<jdk>[16,)</jdk>
|
||||
<jdk>[15,)</jdk>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
@ -87,17 +87,16 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile-java16</id>
|
||||
<id>compile-java15</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<release>16</release>
|
||||
<release>15</release>
|
||||
<compileSourceRoots>
|
||||
<compileSourceRoot>${project.basedir}/src/main/java16</compileSourceRoot>
|
||||
<compileSourceRoot>${project.basedir}/src/main/java15</compileSourceRoot>
|
||||
</compileSourceRoots>
|
||||
<multiReleaseOutput>true</multiReleaseOutput>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@ -117,13 +116,6 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Multi-Release>true</Multi-Release>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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.jose.jwk;
|
||||
|
||||
import static org.keycloak.jose.jwk.JWKUtil.toIntegerBytes;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public abstract class AbstractJWKBuilder {
|
||||
|
||||
public static final KeyUse DEFAULT_PUBLIC_KEY_USE = KeyUse.SIG;
|
||||
|
||||
protected String kid;
|
||||
|
||||
protected String algorithm;
|
||||
|
||||
public JWK rs256(PublicKey key) {
|
||||
this.algorithm = Algorithm.RS256;
|
||||
return rsa(key);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key) {
|
||||
return rsa(key, null, KeyUse.SIG);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, X509Certificate certificate) {
|
||||
return rsa(key, Collections.singletonList(certificate), KeyUse.SIG);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, List<X509Certificate> certificates) {
|
||||
return rsa(key, certificates, null);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, List<X509Certificate> certificates, KeyUse keyUse) {
|
||||
RSAPublicKey rsaKey = (RSAPublicKey) key;
|
||||
|
||||
RSAPublicJWK k = new RSAPublicJWK();
|
||||
|
||||
String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
|
||||
k.setKeyId(kid);
|
||||
k.setKeyType(KeyType.RSA);
|
||||
k.setAlgorithm(algorithm);
|
||||
k.setPublicKeyUse(keyUse == null ? KeyUse.SIG.getSpecName() : keyUse.getSpecName());
|
||||
k.setModulus(Base64Url.encode(toIntegerBytes(rsaKey.getModulus())));
|
||||
k.setPublicExponent(Base64Url.encode(toIntegerBytes(rsaKey.getPublicExponent())));
|
||||
|
||||
if (certificates != null && !certificates.isEmpty()) {
|
||||
String[] certificateChain = new String[certificates.size()];
|
||||
for (int i = 0; i < certificates.size(); i++) {
|
||||
certificateChain[i] = PemUtils.encodeCertificate(certificates.get(i));
|
||||
}
|
||||
k.setX509CertificateChain(certificateChain);
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, KeyUse keyUse) {
|
||||
JWK k = rsa(key);
|
||||
String keyUseString = keyUse == null ? DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName();
|
||||
if (KeyUse.ENC == keyUse) keyUseString = "enc";
|
||||
k.setPublicKeyUse(keyUseString);
|
||||
return k;
|
||||
}
|
||||
|
||||
public JWK ec(Key key) {
|
||||
return ec(key, DEFAULT_PUBLIC_KEY_USE);
|
||||
}
|
||||
|
||||
public JWK ec(Key key, KeyUse keyUse) {
|
||||
return this.ec(key, null, keyUse);
|
||||
}
|
||||
|
||||
public JWK ec(Key key, List<X509Certificate> certificates, KeyUse keyUse) {
|
||||
ECPublicKey ecKey = (ECPublicKey) key;
|
||||
|
||||
ECPublicJWK k = new ECPublicJWK();
|
||||
|
||||
String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
|
||||
int fieldSize = ecKey.getParams().getCurve().getField().getFieldSize();
|
||||
|
||||
k.setKeyId(kid);
|
||||
k.setKeyType(KeyType.EC);
|
||||
k.setAlgorithm(algorithm);
|
||||
k.setPublicKeyUse(keyUse == null ? DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName());
|
||||
k.setCrv("P-" + fieldSize);
|
||||
k.setX(Base64Url.encode(toIntegerBytes(ecKey.getW().getAffineX(), fieldSize)));
|
||||
k.setY(Base64Url.encode(toIntegerBytes(ecKey.getW().getAffineY(), fieldSize)));
|
||||
|
||||
if (certificates != null && !certificates.isEmpty()) {
|
||||
String[] certificateChain = new String[certificates.size()];
|
||||
for (int i = 0; i < certificates.size(); i++) {
|
||||
certificateChain[i] = PemUtils.encodeCertificate(certificates.get(i));
|
||||
}
|
||||
k.setX509CertificateChain(certificateChain);
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
public abstract JWK okp(Key key);
|
||||
|
||||
public abstract JWK okp(Key key, KeyUse keyUse);
|
||||
|
||||
public static byte[] reverseBytes(byte[] array) {
|
||||
if (array == null || array.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int length = array.length;
|
||||
byte[] reversedArray = new byte[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
reversedArray[length - 1 - i] = array[i];
|
||||
}
|
||||
|
||||
return reversedArray;
|
||||
}
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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.jose.jwk;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public abstract class AbstractJWKParser {
|
||||
|
||||
protected JWK jwk;
|
||||
|
||||
public JWK getJwk() {
|
||||
return jwk;
|
||||
}
|
||||
|
||||
public PublicKey toPublicKey() {
|
||||
if (jwk == null) {
|
||||
throw new IllegalStateException("Not possible to convert to the publicKey. The jwk is not set");
|
||||
}
|
||||
String keyType = jwk.getKeyType();
|
||||
if (KeyType.RSA.equals(keyType)) {
|
||||
return createRSAPublicKey();
|
||||
} else if (KeyType.EC.equals(keyType)) {
|
||||
return createECPublicKey();
|
||||
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported keyType " + keyType);
|
||||
}
|
||||
}
|
||||
|
||||
protected PublicKey createECPublicKey() {
|
||||
/* Check if jwk.getOtherClaims return an empty map */
|
||||
if (jwk.getOtherClaims().size() == 0) {
|
||||
throw new RuntimeException("JWK Otherclaims map is empty.");
|
||||
}
|
||||
|
||||
/* Try retrieving the necessary fields */
|
||||
String crv = (String) jwk.getOtherClaims().get(ECPublicJWK.CRV);
|
||||
String xStr = (String) jwk.getOtherClaims().get(ECPublicJWK.X);
|
||||
String yStr = (String) jwk.getOtherClaims().get(ECPublicJWK.Y);
|
||||
|
||||
/* Check if the retrieving of necessary fields success */
|
||||
if (crv == null || xStr == null || yStr == null) {
|
||||
throw new RuntimeException("Fail to retrieve ECPublicJWK.CRV, ECPublicJWK.X or ECPublicJWK.Y field.");
|
||||
}
|
||||
|
||||
BigInteger x = new BigInteger(1, Base64Url.decode(xStr));
|
||||
BigInteger y = new BigInteger(1, Base64Url.decode(yStr));
|
||||
|
||||
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 RuntimeException("Unsupported curve");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
ECPoint point = new ECPoint(x, y);
|
||||
ECParameterSpec params = CryptoIntegration.getProvider().createECParams(name);
|
||||
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
|
||||
|
||||
KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory("ECDSA");
|
||||
return kf.generatePublic(pubKeySpec);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected PublicKey createRSAPublicKey() {
|
||||
BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.MODULUS).toString()));
|
||||
BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.PUBLIC_EXPONENT).toString()));
|
||||
|
||||
try {
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
return kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isKeyTypeSupported(String keyType) {
|
||||
return (RSAPublicJWK.RSA.equals(keyType) || ECPublicJWK.EC.equals(keyType));
|
||||
}
|
||||
|
||||
}
|
||||
35
core/src/main/java/org/keycloak/jose/jwk/EdECUtils.java
Normal file
35
core/src/main/java/org/keycloak/jose/jwk/EdECUtils.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.jose.jwk;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
|
||||
/**
|
||||
* <p>Interface for the EdECUtils that will be implemented only for JDK 15+.</p>
|
||||
*
|
||||
* @author rmartinc
|
||||
*/
|
||||
interface EdECUtils {
|
||||
|
||||
boolean isEdECSupported();
|
||||
|
||||
JWK okp(String kid, String algorithm, Key key, KeyUse keyUse);
|
||||
|
||||
PublicKey createOKPPublicKey(JWK jwk);
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.jose.jwk;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
|
||||
/**
|
||||
* <p>Unsupported implementation for old jdk versions.</p>
|
||||
*
|
||||
* @author rmartinc
|
||||
*/
|
||||
class EdECUtilsUnsupportedImpl implements EdECUtils {
|
||||
|
||||
@Override
|
||||
public boolean isEdECSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JWK okp(String kid, String algorithm, Key key, KeyUse keyUse) {
|
||||
throw new UnsupportedOperationException("EdDSA algorithms not supported in this JDK version");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey createOKPPublicKey(JWK jwk) {
|
||||
throw new UnsupportedOperationException("EdDSA algorithms not supported in this JDK version");
|
||||
}
|
||||
}
|
||||
@ -17,14 +17,49 @@
|
||||
|
||||
package org.keycloak.jose.jwk;
|
||||
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
import static org.keycloak.jose.jwk.JWKUtil.toIntegerBytes;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JWKBuilder extends AbstractJWKBuilder {
|
||||
public class JWKBuilder {
|
||||
|
||||
// internal util class only loaded for jdk versions with EdEC support
|
||||
protected static final EdECUtils EdEC_UTILS;
|
||||
|
||||
static {
|
||||
EdECUtils tmp;
|
||||
try {
|
||||
// check if the impl class for EdEC can be loaded in the runtime
|
||||
tmp = (EdECUtils) Class.forName("org.keycloak.jose.jwk.EdECUtilsImpl")
|
||||
.getDeclaredConstructor().newInstance();
|
||||
} catch(Throwable e) {
|
||||
// not supported implementation
|
||||
tmp = new EdECUtilsUnsupportedImpl();
|
||||
}
|
||||
EdEC_UTILS = tmp;
|
||||
}
|
||||
|
||||
public static final KeyUse DEFAULT_PUBLIC_KEY_USE = KeyUse.SIG;
|
||||
|
||||
protected String kid;
|
||||
|
||||
protected String algorithm;
|
||||
|
||||
private JWKBuilder() {
|
||||
}
|
||||
@ -43,15 +78,95 @@ public class JWKBuilder extends AbstractJWKBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JWK okp(Key key) {
|
||||
// not supported if jdk vesion < 17
|
||||
throw new UnsupportedOperationException("EdDSA algorithms not supported in this JDK version");
|
||||
public JWK rs256(PublicKey key) {
|
||||
this.algorithm = Algorithm.RS256;
|
||||
return rsa(key);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key) {
|
||||
return rsa(key, null, KeyUse.SIG);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, X509Certificate certificate) {
|
||||
return rsa(key, Collections.singletonList(certificate), KeyUse.SIG);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, List<X509Certificate> certificates) {
|
||||
return rsa(key, certificates, null);
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, List<X509Certificate> certificates, KeyUse keyUse) {
|
||||
RSAPublicKey rsaKey = (RSAPublicKey) key;
|
||||
|
||||
RSAPublicJWK k = new RSAPublicJWK();
|
||||
|
||||
String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
|
||||
k.setKeyId(kid);
|
||||
k.setKeyType(KeyType.RSA);
|
||||
k.setAlgorithm(algorithm);
|
||||
k.setPublicKeyUse(keyUse == null ? KeyUse.SIG.getSpecName() : keyUse.getSpecName());
|
||||
k.setModulus(Base64Url.encode(toIntegerBytes(rsaKey.getModulus())));
|
||||
k.setPublicExponent(Base64Url.encode(toIntegerBytes(rsaKey.getPublicExponent())));
|
||||
|
||||
if (certificates != null && !certificates.isEmpty()) {
|
||||
String[] certificateChain = new String[certificates.size()];
|
||||
for (int i = 0; i < certificates.size(); i++) {
|
||||
certificateChain[i] = PemUtils.encodeCertificate(certificates.get(i));
|
||||
}
|
||||
k.setX509CertificateChain(certificateChain);
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
public JWK rsa(Key key, KeyUse keyUse) {
|
||||
JWK k = rsa(key);
|
||||
String keyUseString = keyUse == null ? DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName();
|
||||
if (KeyUse.ENC == keyUse) keyUseString = "enc";
|
||||
k.setPublicKeyUse(keyUseString);
|
||||
return k;
|
||||
}
|
||||
|
||||
public JWK ec(Key key) {
|
||||
return ec(key, DEFAULT_PUBLIC_KEY_USE);
|
||||
}
|
||||
|
||||
public JWK ec(Key key, KeyUse keyUse) {
|
||||
return this.ec(key, null, keyUse);
|
||||
}
|
||||
|
||||
public JWK ec(Key key, List<X509Certificate> certificates, KeyUse keyUse) {
|
||||
ECPublicKey ecKey = (ECPublicKey) key;
|
||||
|
||||
ECPublicJWK k = new ECPublicJWK();
|
||||
|
||||
String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
|
||||
int fieldSize = ecKey.getParams().getCurve().getField().getFieldSize();
|
||||
|
||||
k.setKeyId(kid);
|
||||
k.setKeyType(KeyType.EC);
|
||||
k.setAlgorithm(algorithm);
|
||||
k.setPublicKeyUse(keyUse == null ? DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName());
|
||||
k.setCrv("P-" + fieldSize);
|
||||
k.setX(Base64Url.encode(toIntegerBytes(ecKey.getW().getAffineX(), fieldSize)));
|
||||
k.setY(Base64Url.encode(toIntegerBytes(ecKey.getW().getAffineY(), fieldSize)));
|
||||
|
||||
if (certificates != null && !certificates.isEmpty()) {
|
||||
String[] certificateChain = new String[certificates.size()];
|
||||
for (int i = 0; i < certificates.size(); i++) {
|
||||
certificateChain[i] = PemUtils.encodeCertificate(certificates.get(i));
|
||||
}
|
||||
k.setX509CertificateChain(certificateChain);
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
public JWK okp(Key key) {
|
||||
return okp(key, DEFAULT_PUBLIC_KEY_USE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JWK okp(Key key, KeyUse keyUse) {
|
||||
// not supported if jdk version < 17
|
||||
throw new UnsupportedOperationException("EdDSA algorithms not supported in this JDK version");
|
||||
return EdEC_UTILS.okp(kid, algorithm, key, keyUse);
|
||||
}
|
||||
}
|
||||
|
||||
98
core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
Executable file → Normal file
98
core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
Executable file → Normal file
@ -17,12 +17,25 @@
|
||||
|
||||
package org.keycloak.jose.jwk;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JWKParser extends AbstractJWKParser {
|
||||
public class JWKParser {
|
||||
|
||||
protected JWK jwk;
|
||||
|
||||
private JWKParser() {
|
||||
}
|
||||
@ -48,4 +61,87 @@ public class JWKParser extends AbstractJWKParser {
|
||||
}
|
||||
}
|
||||
|
||||
public JWK getJwk() {
|
||||
return jwk;
|
||||
}
|
||||
|
||||
public PublicKey toPublicKey() {
|
||||
if (jwk == null) {
|
||||
throw new IllegalStateException("Not possible to convert to the publicKey. The jwk is not set");
|
||||
}
|
||||
String keyType = jwk.getKeyType();
|
||||
if (KeyType.RSA.equals(keyType)) {
|
||||
return createRSAPublicKey();
|
||||
} else if (KeyType.EC.equals(keyType)) {
|
||||
return createECPublicKey();
|
||||
} else if (KeyType.OKP.equals(keyType)) {
|
||||
return JWKBuilder.EdEC_UTILS.createOKPPublicKey(jwk);
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported keyType " + keyType);
|
||||
}
|
||||
}
|
||||
|
||||
protected PublicKey createECPublicKey() {
|
||||
/* Check if jwk.getOtherClaims return an empty map */
|
||||
if (jwk.getOtherClaims().size() == 0) {
|
||||
throw new RuntimeException("JWK Otherclaims map is empty.");
|
||||
}
|
||||
|
||||
/* Try retrieving the necessary fields */
|
||||
String crv = (String) jwk.getOtherClaims().get(ECPublicJWK.CRV);
|
||||
String xStr = (String) jwk.getOtherClaims().get(ECPublicJWK.X);
|
||||
String yStr = (String) jwk.getOtherClaims().get(ECPublicJWK.Y);
|
||||
|
||||
/* Check if the retrieving of necessary fields success */
|
||||
if (crv == null || xStr == null || yStr == null) {
|
||||
throw new RuntimeException("Fail to retrieve ECPublicJWK.CRV, ECPublicJWK.X or ECPublicJWK.Y field.");
|
||||
}
|
||||
|
||||
BigInteger x = new BigInteger(1, Base64Url.decode(xStr));
|
||||
BigInteger y = new BigInteger(1, Base64Url.decode(yStr));
|
||||
|
||||
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 RuntimeException("Unsupported curve");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
ECPoint point = new ECPoint(x, y);
|
||||
ECParameterSpec params = CryptoIntegration.getProvider().createECParams(name);
|
||||
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
|
||||
|
||||
KeyFactory kf = CryptoIntegration.getProvider().getKeyFactory("ECDSA");
|
||||
return kf.generatePublic(pubKeySpec);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected PublicKey createRSAPublicKey() {
|
||||
BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.MODULUS).toString()));
|
||||
BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.PUBLIC_EXPONENT).toString()));
|
||||
|
||||
try {
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
return kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isKeyTypeSupported(String keyType) {
|
||||
return (RSAPublicJWK.RSA.equals(keyType) || ECPublicJWK.EC.equals(keyType)
|
||||
|| (JWKBuilder.EdEC_UTILS.isEdECSupported() && OKPPublicJWK.OKP.equals(keyType)));
|
||||
}
|
||||
}
|
||||
|
||||
157
core/src/main/java15/org/keycloak/jose/jwk/EdECUtilsImpl.java
Normal file
157
core/src/main/java15/org/keycloak/jose/jwk/EdECUtilsImpl.java
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.jose.jwk;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.security.spec.EdECPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Optional;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
|
||||
/**
|
||||
* <p>Class that uses Java 15+ EdEC classes and implements the EdECUtils interface.</p>
|
||||
*
|
||||
* @author rmartinc
|
||||
*/
|
||||
class EdECUtilsImpl implements EdECUtils {
|
||||
|
||||
public EdECUtilsImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEdECSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JWK okp(String kid, String algorithm, Key key, KeyUse keyUse) {
|
||||
EdECPublicKey eddsaPublicKey = (EdECPublicKey) key;
|
||||
|
||||
OKPPublicJWK k = new OKPPublicJWK();
|
||||
|
||||
kid = kid != null ? kid : KeyUtils.createKeyId(key);
|
||||
|
||||
k.setKeyId(kid);
|
||||
k.setKeyType(KeyType.OKP);
|
||||
k.setAlgorithm(algorithm);
|
||||
k.setPublicKeyUse(keyUse == null ? JWKBuilder.DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName());
|
||||
k.setCrv(eddsaPublicKey.getParams().getName());
|
||||
|
||||
Optional<String> x = edPublicKeyInJwkRepresentation(eddsaPublicKey);
|
||||
k.setX(x.orElse(""));
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey createOKPPublicKey(JWK jwk) {
|
||||
String x = (String) jwk.getOtherClaims().get(OKPPublicJWK.X);
|
||||
String crv = (String) jwk.getOtherClaims().get(OKPPublicJWK.CRV);
|
||||
// JWK representation "x" of a public key
|
||||
int bytesLength = 0;
|
||||
if (Algorithm.Ed25519.equals(crv)) {
|
||||
bytesLength = 32;
|
||||
} else if (Algorithm.Ed448.equals(crv)) {
|
||||
bytesLength = 57;
|
||||
} else {
|
||||
throw new RuntimeException("Invalid JWK representation of OKP type algorithm");
|
||||
}
|
||||
|
||||
byte[] decodedX = Base64Url.decode(x);
|
||||
if (decodedX.length != bytesLength) {
|
||||
throw new RuntimeException("Invalid JWK representation of OKP type public key");
|
||||
}
|
||||
|
||||
// x-coordinate's parity check shown by MSB(bit) of MSB(byte) of decoded "x": 1 is odd, 0 is even
|
||||
boolean isOddX = false;
|
||||
if ((decodedX[decodedX.length - 1] & -128) != 0) { // 0b10000000
|
||||
isOddX = true;
|
||||
}
|
||||
|
||||
// MSB(bit) of MSB(byte) showing x-coodinate's parity is set to 0
|
||||
decodedX[decodedX.length - 1] &= 127; // 0b01111111
|
||||
|
||||
// both x and y-coordinate in twisted Edwards curve are always 0 or natural number
|
||||
BigInteger y = new BigInteger(1, reverseBytes(decodedX));
|
||||
NamedParameterSpec spec = new NamedParameterSpec(crv);
|
||||
EdECPoint ep = new EdECPoint(isOddX, y);
|
||||
EdECPublicKeySpec keySpec = new EdECPublicKeySpec(spec, ep);
|
||||
|
||||
PublicKey publicKey = null;
|
||||
try {
|
||||
publicKey = KeyFactory.getInstance(crv).generatePublic(keySpec);
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
private static Optional<String> edPublicKeyInJwkRepresentation(EdECPublicKey eddsaPublicKey) {
|
||||
EdECPoint edEcPoint = eddsaPublicKey.getPoint();
|
||||
BigInteger yCoordinate = edEcPoint.getY();
|
||||
|
||||
// JWK representation "x" of a public key
|
||||
int bytesLength = 0;
|
||||
if (Algorithm.Ed25519.equals(eddsaPublicKey.getParams().getName())) {
|
||||
bytesLength = 32;
|
||||
} else if (Algorithm.Ed448.equals(eddsaPublicKey.getParams().getName())) {
|
||||
bytesLength = 57;
|
||||
} else {
|
||||
return Optional.ofNullable(null);
|
||||
}
|
||||
|
||||
// consider the case where yCoordinate.toByteArray() is less than bytesLength due to relatively small value of y-coordinate.
|
||||
byte[] yCoordinateLittleEndianBytes = new byte[bytesLength];
|
||||
|
||||
// convert big endian representation of BigInteger to little endian representation of JWK representation (RFC 8032,8027)
|
||||
byte[] yCoordinateLittleEndian = reverseBytes(yCoordinate.toByteArray());
|
||||
System.arraycopy(yCoordinateLittleEndian, 0, yCoordinateLittleEndianBytes, 0, yCoordinateLittleEndian.length);
|
||||
|
||||
// set a parity of x-coordinate to the most significant bit of the last octet (RFC 8032, 8037)
|
||||
if (edEcPoint.isXOdd()) {
|
||||
yCoordinateLittleEndianBytes[yCoordinateLittleEndianBytes.length - 1] |= -128; // 0b10000000
|
||||
}
|
||||
|
||||
return Optional.ofNullable(Base64Url.encode(yCoordinateLittleEndianBytes));
|
||||
}
|
||||
|
||||
private static byte[] reverseBytes(byte[] array) {
|
||||
if (array == null || array.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int length = array.length;
|
||||
byte[] reversedArray = new byte[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
reversedArray[length - 1 - i] = array[i];
|
||||
}
|
||||
|
||||
return reversedArray;
|
||||
}
|
||||
}
|
||||
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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.jose.jwk;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.Key;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JWKBuilder extends AbstractJWKBuilder {
|
||||
|
||||
private JWKBuilder() {
|
||||
}
|
||||
|
||||
public static JWKBuilder create() {
|
||||
return new JWKBuilder();
|
||||
}
|
||||
|
||||
public JWKBuilder kid(String kid) {
|
||||
this.kid = kid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JWKBuilder algorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JWK okp(Key key) {
|
||||
return okp(key, DEFAULT_PUBLIC_KEY_USE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JWK okp(Key key, KeyUse keyUse) {
|
||||
EdECPublicKey eddsaPublicKey = (EdECPublicKey) key;
|
||||
|
||||
OKPPublicJWK k = new OKPPublicJWK();
|
||||
|
||||
String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
|
||||
|
||||
k.setKeyId(kid);
|
||||
k.setKeyType(KeyType.OKP);
|
||||
k.setAlgorithm(algorithm);
|
||||
k.setPublicKeyUse(keyUse == null ? DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName());
|
||||
k.setCrv(eddsaPublicKey.getParams().getName());
|
||||
|
||||
Optional<String> x = edPublicKeyInJwkRepresentation(eddsaPublicKey);
|
||||
k.setX(x.orElse(""));
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
private Optional<String> edPublicKeyInJwkRepresentation(EdECPublicKey eddsaPublicKey) {
|
||||
EdECPoint edEcPoint = eddsaPublicKey.getPoint();
|
||||
BigInteger yCoordinate = edEcPoint.getY();
|
||||
|
||||
// JWK representation "x" of a public key
|
||||
int bytesLength = 0;
|
||||
if (Algorithm.Ed25519.equals(eddsaPublicKey.getParams().getName())) {
|
||||
bytesLength = 32;
|
||||
} else if (Algorithm.Ed448.equals(eddsaPublicKey.getParams().getName())) {
|
||||
bytesLength = 57;
|
||||
} else {
|
||||
return Optional.ofNullable(null);
|
||||
}
|
||||
|
||||
// consider the case where yCoordinate.toByteArray() is less than bytesLength due to relatively small value of y-coordinate.
|
||||
byte[] yCoordinateLittleEndianBytes = new byte[bytesLength];
|
||||
|
||||
// convert big endian representation of BigInteger to little endian representation of JWK representation (RFC 8032,8027)
|
||||
yCoordinateLittleEndianBytes = Arrays.copyOf(reverseBytes(yCoordinate.toByteArray()), bytesLength);
|
||||
|
||||
// set a parity of x-coordinate to the most significant bit of the last octet (RFC 8032, 8037)
|
||||
if (edEcPoint.isXOdd()) {
|
||||
yCoordinateLittleEndianBytes[yCoordinateLittleEndianBytes.length - 1] |= -128; // 0b10000000
|
||||
}
|
||||
|
||||
return Optional.ofNullable(Base64Url.encode(yCoordinateLittleEndianBytes));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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.jose.jwk;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.security.spec.EdECPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JWKParser extends AbstractJWKParser {
|
||||
|
||||
private JWKParser() {
|
||||
}
|
||||
|
||||
public static JWKParser create() {
|
||||
return new JWKParser();
|
||||
}
|
||||
|
||||
public JWKParser(JWK jwk) {
|
||||
this.jwk = jwk;
|
||||
}
|
||||
|
||||
public static JWKParser create(JWK jwk) {
|
||||
return new JWKParser(jwk);
|
||||
}
|
||||
|
||||
public JWKParser parse(String jwk) {
|
||||
try {
|
||||
this.jwk = JsonSerialization.mapper.readValue(jwk, JWK.class);
|
||||
return this;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey toPublicKey() {
|
||||
if (jwk == null) {
|
||||
throw new IllegalStateException("Not possible to convert to the publicKey. The jwk is not set");
|
||||
}
|
||||
String keyType = jwk.getKeyType();
|
||||
if (KeyType.RSA.equals(keyType)) {
|
||||
return createRSAPublicKey();
|
||||
} else if (KeyType.EC.equals(keyType)) {
|
||||
return createECPublicKey();
|
||||
} else if (KeyType.OKP.equals(keyType)) {
|
||||
return createOKPPublicKey();
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported keyType " + keyType);
|
||||
}
|
||||
}
|
||||
|
||||
private PublicKey createOKPPublicKey() {
|
||||
String x = (String) jwk.getOtherClaims().get(OKPPublicJWK.X);
|
||||
String crv = (String) jwk.getOtherClaims().get(OKPPublicJWK.CRV);
|
||||
// JWK representation "x" of a public key
|
||||
int bytesLength = 0;
|
||||
if (Algorithm.Ed25519.equals(crv)) {
|
||||
bytesLength = 32;
|
||||
} else if (Algorithm.Ed448.equals(crv)) {
|
||||
bytesLength = 57;
|
||||
} else {
|
||||
throw new RuntimeException("Invalid JWK representation of OKP type algorithm");
|
||||
}
|
||||
|
||||
byte[] decodedX = Base64Url.decode(x);
|
||||
if (decodedX.length != bytesLength) {
|
||||
throw new RuntimeException("Invalid JWK representation of OKP type public key");
|
||||
}
|
||||
|
||||
// x-coordinate's parity check shown by MSB(bit) of MSB(byte) of decoded "x": 1 is odd, 0 is even
|
||||
boolean isOddX = false;
|
||||
if ((decodedX[decodedX.length - 1] & -128) != 0) { // 0b10000000
|
||||
isOddX = true;
|
||||
}
|
||||
|
||||
// MSB(bit) of MSB(byte) showing x-coodinate's parity is set to 0
|
||||
decodedX[decodedX.length - 1] &= 127; // 0b01111111
|
||||
|
||||
// both x and y-coordinate in twisted Edwards curve are always 0 or natural number
|
||||
BigInteger y = new BigInteger(1, JWKBuilder.reverseBytes(decodedX));
|
||||
NamedParameterSpec spec = new NamedParameterSpec(crv);
|
||||
EdECPoint ep = new EdECPoint(isOddX, y);
|
||||
EdECPublicKeySpec keySpec = new EdECPublicKeySpec(spec, ep);
|
||||
|
||||
PublicKey publicKey = null;
|
||||
try {
|
||||
publicKey = KeyFactory.getInstance(crv).generatePublic(keySpec);
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeyTypeSupported(String keyType) {
|
||||
return (RSAPublicJWK.RSA.equals(keyType) || ECPublicJWK.EC.equals(keyType) || OKPPublicJWK.OKP.equals(keyType));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user