iterate any attribute in multi-valued RDN to find the correct one (#14283)
Closes #14280
This commit is contained in:
parent
fd28cd2d4b
commit
ba66fe84fa
4 changed files with 108 additions and 9 deletions
|
@ -21,7 +21,9 @@ package org.keycloak.authentication.x509;
|
|||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.Principal;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
|
@ -40,9 +42,12 @@ public abstract class CertificateIdentityExtractorTest {
|
|||
@ClassRule
|
||||
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
|
||||
|
||||
private static final String UPN_CERT_PATH = "/certs/UPN-cert.pem";
|
||||
private static final String ANS_CERT_PATH = "/certs/ANS-cert.pem";
|
||||
|
||||
@Test
|
||||
public void testExtractsCertInPemFormat() throws Exception {
|
||||
X509Certificate x509Certificate = getCertificate();
|
||||
X509Certificate x509Certificate = getCertificate(UPN_CERT_PATH);
|
||||
|
||||
String certificatePem = PemUtils.encodeCertificate(x509Certificate);
|
||||
|
||||
|
@ -56,7 +61,7 @@ public abstract class CertificateIdentityExtractorTest {
|
|||
|
||||
@Test
|
||||
public void testExtractsCertInSubjectDNFormat() throws Exception {
|
||||
X509Certificate x509Certificate = getCertificate();
|
||||
X509Certificate x509Certificate = getCertificate(UPN_CERT_PATH);
|
||||
|
||||
UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getX500NameExtractor("CN", certs -> {
|
||||
return certs[0].getSubjectX500Principal();
|
||||
|
@ -69,7 +74,7 @@ public abstract class CertificateIdentityExtractorTest {
|
|||
public void testX509SubjectAltName_otherName() throws Exception {
|
||||
UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getSubjectAltNameExtractor(0);
|
||||
|
||||
X509Certificate cert = getCertificate();
|
||||
X509Certificate cert = getCertificate(UPN_CERT_PATH);
|
||||
|
||||
Object upn = extractor.extractUserIdentity(new X509Certificate[] { cert});
|
||||
Assert.assertEquals("test-user@some-company-domain", upn);
|
||||
|
@ -80,18 +85,34 @@ public abstract class CertificateIdentityExtractorTest {
|
|||
public void testX509SubjectAltName_email() throws Exception {
|
||||
UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getSubjectAltNameExtractor(1);
|
||||
|
||||
X509Certificate cert = getCertificate();
|
||||
X509Certificate cert = getCertificate(UPN_CERT_PATH);
|
||||
|
||||
Object upn = extractor.extractUserIdentity(new X509Certificate[] { cert});
|
||||
Assert.assertEquals("test@somecompany.com", upn);
|
||||
}
|
||||
|
||||
|
||||
private X509Certificate getCertificate() throws Exception {
|
||||
InputStream is = getClass().getResourceAsStream("/certs/UPN-cert.pem");
|
||||
private X509Certificate getCertificate(String resourceFilename) throws Exception {
|
||||
InputStream is = getClass().getResourceAsStream(resourceFilename);
|
||||
|
||||
String s = StreamUtil.readString(is, Charset.defaultCharset());
|
||||
|
||||
return PemUtils.decodeCertificate(s);
|
||||
}
|
||||
|
||||
|
||||
private static final Function<X509Certificate[], Principal> subject = certs -> {
|
||||
return certs[0].getSubjectX500Principal();
|
||||
};
|
||||
|
||||
|
||||
@Test
|
||||
public void testX509SubjectCommonName() throws Exception {
|
||||
UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getX500NameExtractor("CN", subject);
|
||||
X509Certificate cert = getCertificate(ANS_CERT_PATH);
|
||||
|
||||
Object cn = extractor.extractUserIdentity(new X509Certificate[] { cert });
|
||||
Assert.assertEquals("899700252580", cn);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
48
core/src/test/resources/certs/ANS-cert.pem
Normal file
48
core/src/test/resources/certs/ANS-cert.pem
Normal file
|
@ -0,0 +1,48 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIInDCCBoSgAwIBAgIQY9r/z8gDS9zqPRI2Y+5HPjANBgkqhkiG9w0BAQsFADB/
|
||||
MQswCQYDVQQGEwJGUjETMBEGA1UECgwKQVNJUC1TQU5URTEXMBUGA1UECwwOMDAw
|
||||
MiAxODc1MTI3NTExFzAVBgNVBAsMDklHQy1TQU5URSBURVNUMSkwJwYDVQQDDCBU
|
||||
RVNUIEFDIElHQy1TQU5URSBGT1JUIFBFUlNPTk5FUzAeFw0yMDA3MDExMjQ2NTJa
|
||||
Fw0yMzA3MDExMjQ2NTJaMGkxCzAJBgNVBAYTAkZSMSMwIQYDVQQMDBpNYXNzZXVy
|
||||
LUtpbsOpc2l0aMOpcmFwZXV0ZTE1MA4GA1UEBAwHS0lORS1DSDAOBgNVBCoMB05P
|
||||
UkJFUlQwEwYDVQQDDAw4OTk3MDAyNTI1ODAwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC6dR2AtgriH1JKsEIlS6hmyYnhGozlm8cJtBZ6KZzWGP5bV1Hr
|
||||
f/WZGdKUk1N0xJKnLmIN8hwhq0MfgoPipkkyIAsrxm5cZLm75j3kHv4oXwepBz4R
|
||||
84OO1bYoz/ya0hny9SCLcOnM7esQMb3ElGzNeXbx6WTl5Rz1zI7auV9sNJiiTY+M
|
||||
O4cWs70SwNOlzU77XGEvm4065LZKsIRnsdNnFhXedl5jBFtkr4tpBhxWlUtlXdTh
|
||||
RHLCsMLmSYV20sGzNkrnZQie6dG6b8T3nxNREAWHBNolnYSqB2WmSkxpYAyX2pcB
|
||||
ufZdysa/Vucw6Ub7iWBnS48zezR3JJs3jsVzAgMBAAGjggQoMIIEJDAJBgNVHRME
|
||||
AjAAMB0GA1UdDgQWBBRy+GBUTgi8gcauDvl+vmdpxdct6jAfBgNVHSMEGDAWgBQ6
|
||||
8ef1zuvt943JybKf7dRlKdwf/TAOBgNVHQ8BAf8EBAMCB4AwUwYDVR0gBEwwSjBI
|
||||
Bg0qgXoBgVUBBwIBAQEBMDcwNQYIKwYBBQUHAgEWKWh0dHA6Ly9pZ2Mtc2FudGUu
|
||||
ZXNhbnRlLmdvdXYuZnIvUEMlMjBURVNUMB8GA1UdJQQYMBYGCCsGAQUFBwMCBgor
|
||||
BgEEAYI3FAICMDUGA1UdEQQuMCygKgYKKwYBBAGCNxQCA6AcDBo4Ljk5NzAwMjUy
|
||||
NTgwQGNhcnRlLWNwcy5mcjCCAUAGA1UdHwSCATcwggEzMDygOqA4hjZodHRwOi8v
|
||||
aWdjLXNhbnRlLmVzYW50ZS5nb3V2LmZyL0NSTC9BQ0ktRk8tUFAtVEVTVC5jcmww
|
||||
gfKgge+ggeyGgelsZGFwOi8vYW5udWFpcmUtaWdjLmVzYW50ZS5nb3V2LmZyL2Nu
|
||||
PVRFU1QlMjBBQyUyMElHQy1TQU5URSUyMEZPUlQlMjBQRVJTT05ORVMsb3U9VEVT
|
||||
VCUyMEFDJTIwUkFDSU5FJTIwSUdDLVNBTlRFJTIwRk9SVCxvdT1JR0MtU0FOVEUl
|
||||
MjBURVNULG91PTAwMDIlMjAxODc1MTI3NTEsbz1BU0lQLVNBTlRFLGM9RlI/Y2Vy
|
||||
dGlmaWNhdGVyZXZvY2F0aW9ubGlzdDtiaW5hcnk/YmFzZT9vYmplY3RDbGFzcz1w
|
||||
a2lDQTCB+gYDVR0uBIHyMIHvMIHsoIHpoIHmhoHjbGRhcDovL2FubnVhaXJlLWln
|
||||
Yy5lc2FudGUuZ291di5mci9jbj1URVNUJTIwQUMlMjBJR0MtU0FOVEUlMjBGT1JU
|
||||
JTIwUEVSU09OTkVTLG91PVRFU1QlMjBBQyUyMFJBQ0lORSUyMElHQy1TQU5URSUy
|
||||
MEZPUlQsb3U9SUdDLVNBTlRFJTIwVEVTVCxvdT0wMDAyJTIwMTg3NTEyNzUxLG89
|
||||
QVNJUC1TQU5URSxjPUZSP2RlbHRhcmV2b2NhdGlvbmxpc3Q7YmluYXJ5P2Jhc2U/
|
||||
b2JqZWN0Q2xhc3M9cGtpQ0EwgYAGCCsGAQUFBwEBBHQwcjAmBggrBgEFBQcwAYYa
|
||||
aHR0cDovL29jc3AuZXNhbnRlLmdvdXYuZnIwSAYIKwYBBQUHMAKGPGh0dHA6Ly9p
|
||||
Z2Mtc2FudGUuZXNhbnRlLmdvdXYuZnIvQUMlMjBURVNUL0FDSS1GTy1QUC1URVNU
|
||||
LmNlcjAPBggqgXoBRwECBQQDBAGAMA8GCCqBegFHAQICBAMCAQAwIwYIKoF6AUcB
|
||||
AgMEFxMVODAyNTAwMDAwMS8yODAwMzgzNjI2MA8GCCqBegFHAQIHBAMCAUYwDQYJ
|
||||
KoZIhvcNAQELBQADggIBAKczdjq7BBhOhITDS3IK09hhwpqnjrCRkiwwoDq+f0hz
|
||||
K2WxEh73RGYufDxhg/wY036TRmiAnlxPJe1cvmyVbtmJDBYKhRv4qzyeiHcSwgtc
|
||||
gypOyJ2GpmrwEOPi3tZpBEhj86XKPab9m5WAyCR/jCy6Md2rY+YS5M73mwpUw3MH
|
||||
SUKjq2Ykp+jCqkvPeMC1kLyYpgAFuIAVHuG7H5wauZ7K4w8fiAqxrbwmErQf4eZb
|
||||
6INfCgMTqvnR05sMvUHhKtYiBG4mUtuO1a6ZN0/OPnkVOOSOi+FMg4oUp8VXa5GR
|
||||
X49+AObu6MaRWSQ2ipeapJGtLYozuatvvewhRe1tJ2rxLJ/rUZbzIEMYVzVUsLpV
|
||||
FcnxkL/pjKgOpZxuLuVx7VUGA8Z8QExNPkrz0BIb3/k4EYwkN8f4UjusA6Q341Sg
|
||||
S3p9mnTO1pTiuap9bR/sFtWvGsFdLOyhGM9NDe9gT9d5XwWZLc+SEUnkiwkF5Zm8
|
||||
ibfx+t5CwIXBqJn8701VkQYFX9s4M/biIjm5N7roFS41a+EAR77ubtI5QkmQUP1z
|
||||
GUbbK/SeqyKlMWkbDnKX+vYLBa/GEZEeHePop85ErHyWqvBpsNnZEzd0cfzVcwMy
|
||||
T+HRcNc38tqbleNJZcD1r0tiYr1MYLF7cZrck4oqx08beq9mm3nqunEKi6cWlKvu
|
||||
-----END CERTIFICATE-----
|
|
@ -24,6 +24,7 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
|||
import org.bouncycastle.asn1.ASN1Sequence;
|
||||
import org.bouncycastle.asn1.ASN1TaggedObject;
|
||||
import org.bouncycastle.asn1.DERUTF8String;
|
||||
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
|
||||
import org.bouncycastle.asn1.x500.RDN;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
|
@ -36,9 +37,11 @@ import java.io.ByteArrayInputStream;
|
|||
import java.security.Principal;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
|
@ -55,7 +58,7 @@ public class BCUserIdentityExtractorProvider extends UserIdentityExtractorProvi
|
|||
|
||||
private ASN1ObjectIdentifier x500NameStyle;
|
||||
Function<X509Certificate[],Principal> x500Name;
|
||||
|
||||
|
||||
public X500NameRDNExtractorBCProvider(String attrName, Function<X509Certificate[], Principal> x500Name) {
|
||||
this.x500NameStyle = BCStyle.INSTANCE.attrNameToOID(attrName);
|
||||
this.x500Name = x500Name;
|
||||
|
@ -72,7 +75,19 @@ public class BCUserIdentityExtractorProvider extends UserIdentityExtractorProvi
|
|||
RDN[] rnds = name.getRDNs(x500NameStyle);
|
||||
if (rnds != null && rnds.length > 0) {
|
||||
RDN cn = rnds[0];
|
||||
return IETFUtils.valueToString(cn.getFirst().getValue());
|
||||
if(cn.isMultiValued()){
|
||||
AttributeTypeAndValue[] attributeTypeAndValues = cn.getTypesAndValues();
|
||||
Optional<AttributeTypeAndValue> optionalFirst = Arrays.stream(attributeTypeAndValues).filter(attributeTypeAndValue -> attributeTypeAndValue.getType().getId().equals(x500NameStyle.getId())).findFirst();
|
||||
if(optionalFirst.isPresent()) {
|
||||
return IETFUtils.valueToString(optionalFirst.get().getValue());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return IETFUtils.valueToString(cn.getFirst().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
|||
import org.bouncycastle.asn1.ASN1Sequence;
|
||||
import org.bouncycastle.asn1.ASN1TaggedObject;
|
||||
import org.bouncycastle.asn1.DERUTF8String;
|
||||
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
|
||||
import org.bouncycastle.asn1.x500.RDN;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
|
@ -36,9 +37,11 @@ import java.io.ByteArrayInputStream;
|
|||
import java.security.Principal;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
|
@ -72,7 +75,19 @@ public class BCFIPSUserIdentityExtractorProvider extends UserIdentityExtractorP
|
|||
RDN[] rnds = name.getRDNs(x500NameStyle);
|
||||
if (rnds != null && rnds.length > 0) {
|
||||
RDN cn = rnds[0];
|
||||
return IETFUtils.valueToString(cn.getFirst().getValue());
|
||||
if(cn.isMultiValued()){
|
||||
AttributeTypeAndValue[] attributeTypeAndValues = cn.getTypesAndValues();
|
||||
Optional<AttributeTypeAndValue> optionalFirst = Arrays.stream(attributeTypeAndValues).filter(attributeTypeAndValue -> attributeTypeAndValue.getType().getId().equals(x500NameStyle.getId())).findFirst();
|
||||
if(optionalFirst.isPresent()) {
|
||||
return IETFUtils.valueToString(optionalFirst.get().getValue());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return IETFUtils.valueToString(cn.getFirst().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
Loading…
Reference in a new issue