Incorrect Signature algorithms presented by Client Authenticator
closes #15853 Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
parent
513c00bcd9
commit
dc3b037e3a
11 changed files with 96 additions and 9 deletions
|
@ -34,7 +34,11 @@ public class CryptoInfoRepresentation {
|
||||||
private String cryptoProvider;
|
private String cryptoProvider;
|
||||||
private List<String> supportedKeystoreTypes;
|
private List<String> supportedKeystoreTypes;
|
||||||
|
|
||||||
public static CryptoInfoRepresentation create() {
|
private List<String> clientSignatureSymmetricAlgorithms;
|
||||||
|
|
||||||
|
private List<String> clientSignatureAsymmetricAlgorithms;
|
||||||
|
|
||||||
|
public static CryptoInfoRepresentation create(List<String> clientSignatureSymmetricAlgorithms, List<String> clientSignatureAsymmetricAlgorithms) {
|
||||||
CryptoInfoRepresentation info = new CryptoInfoRepresentation();
|
CryptoInfoRepresentation info = new CryptoInfoRepresentation();
|
||||||
|
|
||||||
CryptoProvider cryptoProvider = CryptoIntegration.getProvider();
|
CryptoProvider cryptoProvider = CryptoIntegration.getProvider();
|
||||||
|
@ -42,6 +46,8 @@ public class CryptoInfoRepresentation {
|
||||||
info.supportedKeystoreTypes = CryptoIntegration.getProvider().getSupportedKeyStoreTypes()
|
info.supportedKeystoreTypes = CryptoIntegration.getProvider().getSupportedKeyStoreTypes()
|
||||||
.map(KeystoreUtil.KeystoreFormat::toString)
|
.map(KeystoreUtil.KeystoreFormat::toString)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
info.clientSignatureSymmetricAlgorithms = clientSignatureSymmetricAlgorithms;
|
||||||
|
info.clientSignatureAsymmetricAlgorithms = clientSignatureAsymmetricAlgorithms;
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -61,4 +67,20 @@ public class CryptoInfoRepresentation {
|
||||||
public void setSupportedKeystoreTypes(List<String> supportedKeystoreTypes) {
|
public void setSupportedKeystoreTypes(List<String> supportedKeystoreTypes) {
|
||||||
this.supportedKeystoreTypes = supportedKeystoreTypes;
|
this.supportedKeystoreTypes = supportedKeystoreTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getClientSignatureSymmetricAlgorithms() {
|
||||||
|
return clientSignatureSymmetricAlgorithms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientSignatureSymmetricAlgorithms(List<String> clientSignatureSymmetricAlgorithms) {
|
||||||
|
this.clientSignatureSymmetricAlgorithms = clientSignatureSymmetricAlgorithms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getClientSignatureAsymmetricAlgorithms() {
|
||||||
|
return clientSignatureAsymmetricAlgorithms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientSignatureAsymmetricAlgorithms(List<String> clientSignatureAsymmetricAlgorithms) {
|
||||||
|
this.clientSignatureAsymmetricAlgorithms = clientSignatureAsymmetricAlgorithms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
"count": "Specifies how many clients can be created using the token",
|
"count": "Specifies how many clients can be created using the token",
|
||||||
"client-authenticator-type": "Client Authenticator used for authentication of this client against Keycloak server",
|
"client-authenticator-type": "Client Authenticator used for authentication of this client against Keycloak server",
|
||||||
"registration-access-token": "The registration access token provides access for clients to the client registration service.",
|
"registration-access-token": "The registration access token provides access for clients to the client registration service.",
|
||||||
"signature-algorithm": "JWA algorithm, which the client needs to use when signing a JWT for authentication. If left blank, the client is allowed to use any algorithm.",
|
"signature-algorithm": "JWA algorithm, which the client needs to use when signing a JWT for authentication. If left blank, the client is allowed to use any appropriate algorithm for the particular client authenticator.",
|
||||||
"anonymousAccessPolicies": "Those Policies are used when the Client Registration Service is invoked by unauthenticated request. This means that the request does not contain Initial Access Token nor Bearer Token.",
|
"anonymousAccessPolicies": "Those Policies are used when the Client Registration Service is invoked by unauthenticated request. This means that the request does not contain Initial Access Token nor Bearer Token.",
|
||||||
"authenticatedAccessPolicies": "Those Policies are used when Client Registration Service is invoked by authenticated request. This means that the request contains Initial Access Token or Bearer Token.",
|
"authenticatedAccessPolicies": "Those Policies are used when Client Registration Service is invoked by authenticated request. This means that the request contains Initial Access Token or Bearer Token.",
|
||||||
"allowRegexComparison": "If OFF, then the Subject DN from given client certificate must exactly match the given DN from the 'Subject DN' property as described in the RFC8705 specification. The Subject DN can be in the RFC2553 or RFC1779 format. If ON, then the Subject DN from given client certificate should match regex specified by 'Subject DN' property.",
|
"allowRegexComparison": "If OFF, then the Subject DN from given client certificate must exactly match the given DN from the 'Subject DN' property as described in the RFC8705 specification. The Subject DN can be in the RFC2553 or RFC1779 format. If ON, then the Subject DN from given client certificate should match regex specified by 'Subject DN' property.",
|
||||||
|
|
|
@ -185,7 +185,9 @@ export const Credentials = ({ client, save, refresh }: CredentialsProps) => {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{(clientAuthenticatorType === "client-jwt" ||
|
{(clientAuthenticatorType === "client-jwt" ||
|
||||||
clientAuthenticatorType === "client-secret-jwt") && <SignedJWT />}
|
clientAuthenticatorType === "client-secret-jwt") && (
|
||||||
|
<SignedJWT clientAuthenticatorType={clientAuthenticatorType} />
|
||||||
|
)}
|
||||||
{clientAuthenticatorType === "client-jwt" && (
|
{clientAuthenticatorType === "client-jwt" && (
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Alert variant="info" isInline title={t("signedJWTConfirm")} />
|
<Alert variant="info" isInline title={t("signedJWTConfirm")} />
|
||||||
|
|
|
@ -10,14 +10,21 @@ import {
|
||||||
|
|
||||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||||
import { HelpItem } from "ui-shared";
|
import { HelpItem } from "ui-shared";
|
||||||
import { convertAttributeNameToForm, sortProviders } from "../../util";
|
import { convertAttributeNameToForm } from "../../util";
|
||||||
import { FormFields } from "../ClientDetails";
|
import { FormFields } from "../ClientDetails";
|
||||||
|
|
||||||
export const SignedJWT = () => {
|
type SignedJWTProps = {
|
||||||
|
clientAuthenticatorType: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SignedJWT = ({ clientAuthenticatorType }: SignedJWTProps) => {
|
||||||
const { control } = useFormContext();
|
const { control } = useFormContext();
|
||||||
const providers = sortProviders(
|
const { cryptoInfo } = useServerInfo();
|
||||||
useServerInfo().providers!.clientSignature.providers
|
const providers =
|
||||||
);
|
clientAuthenticatorType === "client-jwt"
|
||||||
|
? cryptoInfo?.clientSignatureAsymmetricAlgorithms ?? []
|
||||||
|
: cryptoInfo?.clientSignatureSymmetricAlgorithms ?? [];
|
||||||
|
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
|
|
||||||
const [open, isOpen] = useState(false);
|
const [open, isOpen] = useState(false);
|
||||||
|
|
|
@ -73,4 +73,6 @@ export interface ProtocolMapperTypeRepresentation {
|
||||||
export interface CryptoInfoRepresentation {
|
export interface CryptoInfoRepresentation {
|
||||||
cryptoProvider: string;
|
cryptoProvider: string;
|
||||||
supportedKeystoreTypes: string[];
|
supportedKeystoreTypes: string[];
|
||||||
|
clientSignatureSymmetricAlgorithms: string[];
|
||||||
|
clientSignatureAsymmetricAlgorithms: string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,4 +28,8 @@ public interface ClientSignatureVerifierProvider extends Provider {
|
||||||
@Override
|
@Override
|
||||||
default void close() {
|
default void close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getAlgorithm();
|
||||||
|
|
||||||
|
boolean isAsymmetricAlgorithm();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,4 +35,13 @@ public class AsymmetricClientSignatureVerifierProvider implements ClientSignatur
|
||||||
return new ClientAsymmetricSignatureVerifierContext(session, client, input);
|
return new ClientAsymmetricSignatureVerifierContext(session, client, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAsymmetricAlgorithm() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,4 +18,14 @@ public class ECDSAClientSignatureVerifierProvider implements ClientSignatureVeri
|
||||||
public SignatureVerifierContext verifier(ClientModel client, JWSInput input) throws VerificationException {
|
public SignatureVerifierContext verifier(ClientModel client, JWSInput input) throws VerificationException {
|
||||||
return new ClientECDSASignatureVerifierContext(session, client, input);
|
return new ClientECDSASignatureVerifierContext(session, client, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAsymmetricAlgorithm() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,4 +34,14 @@ public class MacSecretClientSignatureVerifierProvider implements ClientSignature
|
||||||
public SignatureVerifierContext verifier(ClientModel client, JWSInput input) throws VerificationException {
|
public SignatureVerifierContext verifier(ClientModel client, JWSInput input) throws VerificationException {
|
||||||
return new ClientMacSignatureVerifierContext(session, client, algorithm);
|
return new ClientMacSignatureVerifierContext(session, client, algorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAsymmetricAlgorithm() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.component.ComponentFactory;
|
import org.keycloak.component.ComponentFactory;
|
||||||
|
import org.keycloak.crypto.ClientSignatureVerifierProvider;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.events.admin.ResourceType;
|
import org.keycloak.events.admin.ResourceType;
|
||||||
|
@ -96,7 +97,21 @@ public class ServerInfoAdminResource {
|
||||||
info.setSystemInfo(SystemInfoRepresentation.create(session.getKeycloakSessionFactory().getServerStartupTimestamp()));
|
info.setSystemInfo(SystemInfoRepresentation.create(session.getKeycloakSessionFactory().getServerStartupTimestamp()));
|
||||||
info.setMemoryInfo(MemoryInfoRepresentation.create());
|
info.setMemoryInfo(MemoryInfoRepresentation.create());
|
||||||
info.setProfileInfo(ProfileInfoRepresentation.create());
|
info.setProfileInfo(ProfileInfoRepresentation.create());
|
||||||
info.setCryptoInfo(CryptoInfoRepresentation.create());
|
|
||||||
|
// True - asymmetric algorithms, false - symmetric algorithms
|
||||||
|
Map<Boolean, List<String>> algorithms = session.getAllProviders(ClientSignatureVerifierProvider.class).stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
ClientSignatureVerifierProvider::isAsymmetricAlgorithm,
|
||||||
|
clientSignatureVerifier -> Collections.singletonList(clientSignatureVerifier.getAlgorithm()),
|
||||||
|
(l1, l2) -> listCombiner(l1, l2)
|
||||||
|
.stream()
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList()),
|
||||||
|
HashMap::new
|
||||||
|
)
|
||||||
|
);
|
||||||
|
info.setCryptoInfo(CryptoInfoRepresentation.create(algorithms.get(false), algorithms.get(true)));
|
||||||
|
|
||||||
setSocialProviders(info);
|
setSocialProviders(info);
|
||||||
setIdentityProviders(info);
|
setIdentityProviders(info);
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.testsuite.admin;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.common.Version;
|
import org.keycloak.common.Version;
|
||||||
|
import org.keycloak.crypto.Algorithm;
|
||||||
import org.keycloak.keys.Attributes;
|
import org.keycloak.keys.Attributes;
|
||||||
import org.keycloak.keys.GeneratedRsaKeyProviderFactory;
|
import org.keycloak.keys.GeneratedRsaKeyProviderFactory;
|
||||||
import org.keycloak.keys.KeyProvider;
|
import org.keycloak.keys.KeyProvider;
|
||||||
|
@ -68,6 +69,11 @@ public class ServerInfoTest extends AbstractKeycloakTest {
|
||||||
assertNotNull(info.getSystemInfo());
|
assertNotNull(info.getSystemInfo());
|
||||||
assertNotNull(info.getCryptoInfo());
|
assertNotNull(info.getCryptoInfo());
|
||||||
Assert.assertNames(info.getCryptoInfo().getSupportedKeystoreTypes(), KeystoreUtils.getSupportedKeystoreTypes());
|
Assert.assertNames(info.getCryptoInfo().getSupportedKeystoreTypes(), KeystoreUtils.getSupportedKeystoreTypes());
|
||||||
|
Assert.assertNames(info.getCryptoInfo().getClientSignatureSymmetricAlgorithms(), Algorithm.HS256, Algorithm.HS384, Algorithm.HS512);
|
||||||
|
Assert.assertNames(info.getCryptoInfo().getClientSignatureAsymmetricAlgorithms(),
|
||||||
|
Algorithm.ES256, Algorithm.ES384, Algorithm.ES512,
|
||||||
|
Algorithm.PS256, Algorithm.PS384, Algorithm.PS512,
|
||||||
|
Algorithm.RS256, Algorithm.RS384, Algorithm.RS512);
|
||||||
|
|
||||||
ComponentTypeRepresentation rsaGeneratedProviderInfo = info.getComponentTypes().get(KeyProvider.class.getName())
|
ComponentTypeRepresentation rsaGeneratedProviderInfo = info.getComponentTypes().get(KeyProvider.class.getName())
|
||||||
.stream()
|
.stream()
|
||||||
|
|
Loading…
Reference in a new issue