keycloak-uncached
Changes
services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java 4(+4 -0)
services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java 2(+2 -0)
Details
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java
index d5c6b60..742454e 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java
@@ -57,6 +57,7 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth
public static final String MAPPING_SOURCE_SELECTION = "x509-cert-auth.mapping-source-selection";
public static final String MAPPING_SOURCE_CERT_SUBJECTDN = "Match SubjectDN using regular expression";
public static final String MAPPING_SOURCE_CERT_SUBJECTDN_EMAIL = "Subject's e-mail";
+ public static final String MAPPING_SOURCE_CERT_SUBJECTALTNAME_EMAIL = "Subject's Alternative Name E-mail";
public static final String MAPPING_SOURCE_CERT_SUBJECTDN_CN = "Subject's Common Name";
public static final String MAPPING_SOURCE_CERT_ISSUERDN = "Match IssuerDN using regular expression";
public static final String MAPPING_SOURCE_CERT_ISSUERDN_EMAIL = "Issuer's e-mail";
@@ -146,6 +147,9 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth
.either(UserIdentityExtractor.getX500NameExtractor(BCStyle.EmailAddress, subject))
.or(UserIdentityExtractor.getX500NameExtractor(BCStyle.E, subject));
break;
+ case SUBJECTALTNAME_EMAIL:
+ extractor = UserIdentityExtractor.getSubjectAltNameExtractor(1);
+ break;
case ISSUERDN_CN:
extractor = UserIdentityExtractor.getX500NameExtractor(BCStyle.CN, issuer);
break;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java
index 03601e2..29af551 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java
@@ -43,6 +43,7 @@ import static org.keycloak.authentication.authenticators.x509.AbstractX509Client
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_ISSUERDN_CN;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_ISSUERDN_EMAIL;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_SERIALNUMBER;
+import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_SUBJECTALTNAME_EMAIL;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_SUBJECTDN;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_SUBJECTDN_CN;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_SUBJECTDN_EMAIL;
@@ -68,6 +69,7 @@ public abstract class AbstractX509ClientCertificateAuthenticatorFactory implemen
private static final String[] mappingSources = {
MAPPING_SOURCE_CERT_SUBJECTDN,
MAPPING_SOURCE_CERT_SUBJECTDN_EMAIL,
+ MAPPING_SOURCE_CERT_SUBJECTALTNAME_EMAIL,
MAPPING_SOURCE_CERT_SUBJECTDN_CN,
MAPPING_SOURCE_CERT_ISSUERDN,
MAPPING_SOURCE_CERT_ISSUERDN_EMAIL,
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/UserIdentityExtractor.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/UserIdentityExtractor.java
index ef29aec..881fdb7 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/UserIdentityExtractor.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/UserIdentityExtractor.java
@@ -25,7 +25,11 @@ import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.keycloak.services.ServicesLogger;
+import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -92,6 +96,52 @@ public abstract class UserIdentityExtractor {
}
}
+ /**
+ * Extracts the subject identifier from the subjectAltName extension.
+ */
+ static class SubjectAltNameExtractor extends UserIdentityExtractor {
+
+ private final int generalName;
+
+ /**
+ * Creates a new instance
+ *
+ * @param generalName an integer representing the general name. See {@link X509Certificate#getSubjectAlternativeNames()}
+ */
+ SubjectAltNameExtractor(int generalName) {
+ this.generalName = generalName;
+ }
+
+ @Override
+ public Object extractUserIdentity(X509Certificate[] certs) {
+ if (certs == null || certs.length == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ try {
+ Collection<List<?>> subjectAlternativeNames = certs[0].getSubjectAlternativeNames();
+
+ if (subjectAlternativeNames == null) {
+ return null;
+ }
+
+ Iterator<List<?>> iterator = subjectAlternativeNames.iterator();
+
+ while (iterator.hasNext()) {
+ List<?> next = iterator.next();
+
+ if (Integer.class.cast(next.get(0)) == generalName) {
+ return next.get(1);
+ }
+ }
+ } catch (CertificateParsingException cause) {
+ logger.errorf(cause, "Failed to obtain identity from subjectAltName extension");
+ }
+
+ return null;
+ }
+ }
+
static class PatternMatcher extends UserIdentityExtractor {
private final String _pattern;
private final Function<X509Certificate[],String> _f;
@@ -143,6 +193,16 @@ public abstract class UserIdentityExtractor {
return new X500NameRDNExtractor(identifier, x500Name);
}
+ /**
+ * Obtains the subjectAltName given a <code>generalName</code>.
+ *
+ * @param generalName an integer representing the general name. See {@link X509Certificate#getSubjectAlternativeNames()}
+ * @return the value from the subjectAltName extension
+ */
+ public static SubjectAltNameExtractor getSubjectAltNameExtractor(int generalName) {
+ return new SubjectAltNameExtractor(generalName);
+ }
+
public static OrBuilder either(UserIdentityExtractor extractor) {
return new OrBuilder(extractor);
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java
index 87c60b4..a738f42 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java
@@ -60,6 +60,7 @@ public class X509AuthenticatorConfigModel extends AuthenticatorConfigModel {
ISSUERDN(MAPPING_SOURCE_CERT_ISSUERDN),
SUBJECTDN_CN(MAPPING_SOURCE_CERT_SUBJECTDN_CN),
SUBJECTDN_EMAIL(MAPPING_SOURCE_CERT_SUBJECTDN_EMAIL),
+ SUBJECTALTNAME_EMAIL(MAPPING_SOURCE_CERT_SUBJECTALTNAME_EMAIL),
SUBJECTDN(MAPPING_SOURCE_CERT_SUBJECTDN);
private String name;