forked from keycloak/keycloak
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Load client keys using SubjectPublicKeyInfo and upload jwks type into…
… the jwks attributes for OIDC ones Closes keycloak#33820 Signed-off-by: rmartinc <[email protected]>
- Loading branch information
Showing
6 changed files
with
136 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,14 +18,20 @@ | |
package org.keycloak.services.util; | ||
|
||
import org.keycloak.models.ClientModel; | ||
import org.keycloak.models.ModelException; | ||
import org.keycloak.models.utils.KeycloakModelUtils; | ||
import org.keycloak.representations.idm.CertificateRepresentation; | ||
import org.keycloak.representations.idm.ClientRepresentation; | ||
|
||
import java.io.IOException; | ||
import java.security.PublicKey; | ||
import java.security.cert.X509Certificate; | ||
import java.util.HashMap; | ||
import org.keycloak.jose.jwk.JSONWebKeySet; | ||
import org.keycloak.jose.jwk.JWK; | ||
import org.keycloak.jose.jwk.JWKParser; | ||
import org.keycloak.protocol.oidc.OIDCConfigAttributes; | ||
import org.keycloak.protocol.oidc.OIDCLoginProtocol; | ||
import org.keycloak.util.JWKSUtils; | ||
import org.keycloak.util.JsonSerialization; | ||
|
||
/** | ||
* @author <a href="mailto:[email protected]">Marek Posolda</a> | ||
|
@@ -48,6 +54,11 @@ public static CertificateRepresentation getCertificateFromClient(ClientModel cli | |
String publicKeyAttribute = attributePrefix + "." + PUBLIC_KEY; | ||
String kidAttribute = attributePrefix + "." + KID; | ||
|
||
if (OIDCLoginProtocol.LOGIN_PROTOCOL.equals(client.getProtocol()) | ||
&& Boolean.parseBoolean(client.getAttribute(OIDCConfigAttributes.USE_JWKS_STRING))) { | ||
return jwksStringToSigCertificateRepresentation(client.getAttribute(OIDCConfigAttributes.JWKS_STRING)); | ||
} | ||
|
||
CertificateRepresentation rep = new CertificateRepresentation(); | ||
rep.setCertificate(client.getAttribute(certificateAttribute)); | ||
rep.setPublicKey(client.getAttribute(publicKeyAttribute)); | ||
|
@@ -57,13 +68,30 @@ public static CertificateRepresentation getCertificateFromClient(ClientModel cli | |
return rep; | ||
} | ||
|
||
public static CertificateRepresentation jwksStringToSigCertificateRepresentation(String jwks) { | ||
if (jwks == null) { | ||
throw new IllegalStateException("The jwks is null!"); | ||
} | ||
|
||
public static void updateClientModelCertificateInfo(ClientModel client, CertificateRepresentation rep, String attributePrefix) { | ||
String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY; | ||
String certificateAttribute = attributePrefix + "." + X509CERTIFICATE; | ||
String publicKeyAttribute = attributePrefix + "." + PUBLIC_KEY; | ||
String kidAttribute = attributePrefix + "." + KID; | ||
try { | ||
JSONWebKeySet keySet = JsonSerialization.readValue(jwks, JSONWebKeySet.class); | ||
JWK publicKeyJwk = JWKSUtils.getKeyForUse(keySet, JWK.Use.SIG); | ||
if (publicKeyJwk == null) { | ||
throw new IllegalStateException("Certificate not found for use sig"); | ||
} | ||
|
||
PublicKey publicKey = JWKParser.create(publicKeyJwk).toPublicKey(); | ||
String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey); | ||
CertificateRepresentation info = new CertificateRepresentation(); | ||
info.setPublicKey(publicKeyPem); | ||
info.setKid(publicKeyJwk.getKeyId()); | ||
return info; | ||
} catch (IOException e) { | ||
throw new IllegalStateException("Invalid jwks representation!", e); | ||
} | ||
} | ||
|
||
public static void updateClientModelCertificateInfo(ClientModel client, CertificateRepresentation rep, String attributePrefix) { | ||
if (rep.getPublicKey() == null && rep.getCertificate() == null) { | ||
throw new IllegalStateException("Both certificate and publicKey are null!"); | ||
} | ||
|
@@ -72,10 +100,42 @@ public static void updateClientModelCertificateInfo(ClientModel client, Certific | |
throw new IllegalStateException("Both certificate and publicKey are not null!"); | ||
} | ||
|
||
String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY; | ||
String certificateAttribute = attributePrefix + "." + X509CERTIFICATE; | ||
String publicKeyAttribute = attributePrefix + "." + PUBLIC_KEY; | ||
String kidAttribute = attributePrefix + "." + KID; | ||
|
||
setOrRemoveAttr(client, privateKeyAttribute, rep.getPrivateKey()); | ||
setOrRemoveAttr(client, publicKeyAttribute, rep.getPublicKey()); | ||
setOrRemoveAttr(client, certificateAttribute, rep.getCertificate()); | ||
setOrRemoveAttr(client, kidAttribute, rep.getKid()); | ||
|
||
if (OIDCLoginProtocol.LOGIN_PROTOCOL.equals(client.getProtocol())) { | ||
setOrRemoveAttr(client, OIDCConfigAttributes.USE_JWKS_STRING, null); | ||
setOrRemoveAttr(client, OIDCConfigAttributes.JWKS_STRING, null); | ||
} | ||
} | ||
|
||
public static void updateClientModelJwksString(ClientModel client, String attributePrefix, String jwks) { | ||
if (jwks == null) { | ||
throw new IllegalStateException("jwks string is null!"); | ||
} | ||
|
||
if (!OIDCLoginProtocol.LOGIN_PROTOCOL.equals(client.getProtocol())) { | ||
throw new IllegalStateException("jwks can only be set for OIDC clients!"); | ||
} | ||
|
||
String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY; | ||
String certificateAttribute = attributePrefix + "." + X509CERTIFICATE; | ||
String publicKeyAttribute = attributePrefix + "." + PUBLIC_KEY; | ||
String kidAttribute = attributePrefix + "." + KID; | ||
|
||
setOrRemoveAttr(client, privateKeyAttribute, null); | ||
setOrRemoveAttr(client, publicKeyAttribute, null); | ||
setOrRemoveAttr(client, certificateAttribute, null); | ||
setOrRemoveAttr(client, kidAttribute, null); | ||
setOrRemoveAttr(client, OIDCConfigAttributes.USE_JWKS_STRING, Boolean.TRUE.toString()); | ||
setOrRemoveAttr(client, OIDCConfigAttributes.JWKS_STRING, jwks); | ||
} | ||
|
||
private static void setOrRemoveAttr(ClientModel client, String attrName, String attrValue) { | ||
|
Oops, something went wrong.