parent
fbae2251e1
commit
ceea11d044
9 changed files with 286 additions and 55 deletions
|
@ -37,6 +37,7 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.crypto.CertificateUtilsProvider;
|
import org.keycloak.common.crypto.CertificateUtilsProvider;
|
||||||
import org.wildfly.security.asn1.ASN1;
|
import org.wildfly.security.asn1.ASN1;
|
||||||
import org.wildfly.security.asn1.DERDecoder;
|
import org.wildfly.security.asn1.DERDecoder;
|
||||||
|
@ -60,6 +61,8 @@ import org.wildfly.security.x500.cert.X509CertificateExtension;
|
||||||
*/
|
*/
|
||||||
public class ElytronCertificateUtils implements CertificateUtilsProvider {
|
public class ElytronCertificateUtils implements CertificateUtilsProvider {
|
||||||
|
|
||||||
|
Logger log = Logger.getLogger(getClass());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates version 3 {@link java.security.cert.X509Certificate}.
|
* Generates version 3 {@link java.security.cert.X509Certificate}.
|
||||||
*
|
*
|
||||||
|
@ -249,16 +252,29 @@ public class ElytronCertificateUtils implements CertificateUtilsProvider {
|
||||||
case ASN1.UTF8_STRING_TYPE:
|
case ASN1.UTF8_STRING_TYPE:
|
||||||
distPointUrls.add(der.decodeUtf8String());
|
distPointUrls.add(der.decodeUtf8String());
|
||||||
break;
|
break;
|
||||||
case 0xa0:
|
case 0xa0: // Decode CRLDistributionPoint FullName list
|
||||||
der.decodeImplicit(0xa0);
|
der.startExplicit(0xa0);
|
||||||
byte[] edata = der.decodeOctetString();
|
break;
|
||||||
while(!Character.isLetterOrDigit(edata[0])) {
|
case 0x86: // Decode CRLDistributionPoint FullName
|
||||||
edata = Arrays.copyOfRange(edata, 1, edata.length);
|
der.decodeImplicit(0x86);
|
||||||
}
|
distPointUrls.add(der.decodeOctetStringAsString());
|
||||||
distPointUrls.add(new String(edata));
|
log.debug("Adding Dist point name: " + distPointUrls.get(distPointUrls.size()-1));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
der.skipElement();
|
der.skipElement();
|
||||||
|
}
|
||||||
|
// Check to see if there is another sequence to process
|
||||||
|
try {
|
||||||
|
if(!der.hasNextElement() && der.peekType() == ASN1.SEQUENCE_TYPE) {
|
||||||
|
der.startSequence();
|
||||||
|
} else if (!der.hasNextElement() && der.peekType() == 0xa0) {
|
||||||
|
der.startExplicit(0xa0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(Exception e) {
|
||||||
|
// Just log this error. Likely the Dist points have been parsed, but
|
||||||
|
// the end of the cert is failing to parse.
|
||||||
|
log.warn("There is an issue parsing the certificate for Distribution Points", e);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.crypto.elytron;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.crypto.ECDSACryptoProvider;
|
import org.keycloak.common.crypto.ECDSACryptoProvider;
|
||||||
import org.wildfly.security.asn1.DERDecoder;
|
import org.wildfly.security.asn1.DERDecoder;
|
||||||
import org.wildfly.security.asn1.DEREncoder;
|
import org.wildfly.security.asn1.DEREncoder;
|
||||||
|
@ -28,6 +29,8 @@ import org.wildfly.security.asn1.DEREncoder;
|
||||||
*/
|
*/
|
||||||
public class ElytronECDSACryptoProvider implements ECDSACryptoProvider {
|
public class ElytronECDSACryptoProvider implements ECDSACryptoProvider {
|
||||||
|
|
||||||
|
Logger log = Logger.getLogger(getClass());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] concatenatedRSToASN1DER(final byte[] signature, int signLength) throws IOException {
|
public byte[] concatenatedRSToASN1DER(final byte[] signature, int signLength) throws IOException {
|
||||||
int len = signLength / 2;
|
int len = signLength / 2;
|
||||||
|
@ -45,6 +48,7 @@ public class ElytronECDSACryptoProvider implements ECDSACryptoProvider {
|
||||||
seq.startSequence();
|
seq.startSequence();
|
||||||
seq.encodeInteger(rBigInteger);
|
seq.encodeInteger(rBigInteger);
|
||||||
seq.encodeInteger(sBigInteger);
|
seq.encodeInteger(sBigInteger);
|
||||||
|
seq.endSequence();
|
||||||
|
|
||||||
return seq.getEncoded();
|
return seq.getEncoded();
|
||||||
|
|
||||||
|
@ -56,13 +60,35 @@ public class ElytronECDSACryptoProvider implements ECDSACryptoProvider {
|
||||||
|
|
||||||
DERDecoder der = new DERDecoder(derEncodedSignatureValue);
|
DERDecoder der = new DERDecoder(derEncodedSignatureValue);
|
||||||
der.startSequence();
|
der.startSequence();
|
||||||
byte[] r = der.decodeInteger().toByteArray();
|
byte[] r = convertToBytes(der.decodeInteger(),len);
|
||||||
byte[] s = der.decodeInteger().toByteArray();
|
byte[] s = convertToBytes(der.decodeInteger(),len);
|
||||||
|
der.endSequence();
|
||||||
byte[] concatenatedSignatureValue = new byte[signLength];
|
byte[] concatenatedSignatureValue = new byte[signLength];
|
||||||
|
|
||||||
System.arraycopy(r, 0, concatenatedSignatureValue, 0, len);
|
System.arraycopy(r, 0, concatenatedSignatureValue, 0, len);
|
||||||
System.arraycopy(s, 0, concatenatedSignatureValue, len, len);
|
System.arraycopy(s, 0, concatenatedSignatureValue, len, len);
|
||||||
|
|
||||||
return concatenatedSignatureValue;
|
return concatenatedSignatureValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If byte array length doesn't match expected length, copy to new
|
||||||
|
// byte array of the expected length
|
||||||
|
private byte[] convertToBytes(BigInteger decodeInteger, int len) {
|
||||||
|
|
||||||
|
byte[] bytes = decodeInteger.toByteArray();
|
||||||
|
|
||||||
|
if(len < bytes.length) {
|
||||||
|
log.debug("Decoded integer byte length greater than expected.");
|
||||||
|
byte[] t = new byte[len];
|
||||||
|
System.arraycopy(bytes, bytes.length - len, t, 0, len);
|
||||||
|
bytes = t;
|
||||||
|
} else if (len > bytes.length) {
|
||||||
|
log.debug("Decoded integer byte length less than expected.");
|
||||||
|
byte[] t = new byte[len];
|
||||||
|
System.arraycopy(bytes, 0, t, len - bytes.length, bytes.length);
|
||||||
|
bytes = t;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,24 +16,42 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.crypto.elytron;
|
package org.keycloak.crypto.elytron;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CRLReason;
|
import java.security.cert.CRLReason;
|
||||||
|
import java.security.cert.CertPath;
|
||||||
|
import java.security.cert.CertPathBuilder;
|
||||||
|
import java.security.cert.CertPathBuilderException;
|
||||||
|
import java.security.cert.CertPathValidator;
|
||||||
import java.security.cert.CertPathValidatorException;
|
import java.security.cert.CertPathValidatorException;
|
||||||
|
import java.security.cert.CertPathValidatorResult;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.PKIXBuilderParameters;
|
||||||
|
import java.security.cert.PKIXCertPathValidatorResult;
|
||||||
|
import java.security.cert.PKIXParameters;
|
||||||
|
import java.security.cert.PKIXRevocationChecker;
|
||||||
|
import java.security.cert.X509CertSelector;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.common.util.PemUtils;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.utils.OCSPProvider;
|
import org.keycloak.utils.OCSPProvider;
|
||||||
import org.wildfly.security.asn1.ASN1;
|
import org.wildfly.security.asn1.ASN1;
|
||||||
|
@ -61,27 +79,42 @@ public class ElytronOCSPProvider extends OCSPProvider {
|
||||||
* @throws CertPathValidatorException
|
* @throws CertPathValidatorException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected OCSPRevocationStatus check(KeycloakSession session, X509Certificate cert, X509Certificate issuerCertificate, List<URI> responderURIs, X509Certificate responderCert, Date date) throws CertPathValidatorException {
|
protected OCSPRevocationStatus check(KeycloakSession session, X509Certificate cert,
|
||||||
|
X509Certificate issuerCertificate, List<URI> responderURIs, X509Certificate responderCert, Date date)
|
||||||
|
throws CertPathValidatorException {
|
||||||
if (responderURIs == null || responderURIs.size() == 0)
|
if (responderURIs == null || responderURIs.size() == 0)
|
||||||
throw new IllegalArgumentException("Need at least one responder");
|
throw new IllegalArgumentException("Need at least one responder");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
trustStore.load(null, "pass".toCharArray());
|
trustStore.load(null, "pass".toCharArray());
|
||||||
trustStore.setCertificateEntry("trust", cert);
|
trustStore.setCertificateEntry("trust", issuerCertificate);
|
||||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
|
||||||
|
|
||||||
|
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
|
||||||
X509RevocationTrustManager trustMgr = X509RevocationTrustManager.builder()
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
.setOcspResponderCert(responderCert)
|
CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
|
||||||
.setTrustStore(trustStore)
|
PKIXRevocationChecker rc = (PKIXRevocationChecker) cpb.getRevocationChecker();
|
||||||
.setTrustManagerFactory(trustManagerFactory)
|
X509CertSelector certSelector = new X509CertSelector();
|
||||||
.build()
|
|
||||||
;
|
|
||||||
|
|
||||||
X509Certificate[] certs = { cert };
|
X509Certificate[] certs = { cert };
|
||||||
trustMgr.checkClientTrusted(certs, cert.getType());
|
certSelector.setCertificate(cert);
|
||||||
} catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException e) {
|
certSelector.setCertificateValid(date);
|
||||||
|
|
||||||
|
CertPath cp = cf.generateCertPath(Arrays.asList(certs));
|
||||||
|
|
||||||
|
PKIXParameters params = new PKIXBuilderParameters(trustStore, certSelector);
|
||||||
|
|
||||||
|
rc.setOcspResponder(responderURIs.get(0));
|
||||||
|
rc.setOcspResponderCert(responderCert);
|
||||||
|
rc.setOptions(EnumSet.noneOf(PKIXRevocationChecker.Option.class));
|
||||||
|
params.setRevocationEnabled(false);
|
||||||
|
params.addCertPathChecker(rc);
|
||||||
|
|
||||||
|
PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
|
||||||
|
logger.debug("Certificate validated by CA: " + result.getTrustAnchor().getCAName());
|
||||||
|
|
||||||
|
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | CertificateException | IOException
|
||||||
|
| KeyStoreException e) {
|
||||||
logger.warn("OSCP Response check failed.", e);
|
logger.warn("OSCP Response check failed.", e);
|
||||||
return unknownStatus();
|
return unknownStatus();
|
||||||
}
|
}
|
||||||
|
@ -155,6 +188,7 @@ public class ElytronOCSPProvider extends OCSPProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.warn("OCSP Responder URIs" + Arrays.toString(responderURIs.toArray()));
|
||||||
return responderURIs;
|
return responderURIs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ import org.wildfly.security.x500.principal.X500AttributePrincipalDecoder;
|
||||||
*/
|
*/
|
||||||
public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractorProvider {
|
public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractorProvider {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ElytronUserIdentityExtractorProvider.class.getName());
|
private Logger log = Logger.getLogger(this.getClass());
|
||||||
|
|
||||||
class X500NameRDNExtractorElytronProvider extends X500NameRDNExtractor {
|
class X500NameRDNExtractorElytronProvider extends X500NameRDNExtractor {
|
||||||
|
|
||||||
|
@ -47,8 +47,13 @@ public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractor
|
||||||
Function<X509Certificate[],Principal> x500Name;
|
Function<X509Certificate[],Principal> x500Name;
|
||||||
|
|
||||||
public X500NameRDNExtractorElytronProvider(String attrName, Function<X509Certificate[], Principal> x500Name) {
|
public X500NameRDNExtractorElytronProvider(String attrName, Function<X509Certificate[], Principal> x500Name) {
|
||||||
//this.x500NameStyle = BCStyle.INSTANCE.attrNameToOID(attrName);
|
// The OidsUtil fails to map 'EmailAddress', instead 'E' is mapped to the OID.
|
||||||
|
// TODO: Open an issue with wildfly-elytron to include 'EmailAddress' in the oid mapping
|
||||||
|
if(attrName.equals("EmailAddress")) {
|
||||||
|
attrName = "E";
|
||||||
|
}
|
||||||
this.x500NameStyle = OidsUtil.attributeNameToOid(OidsUtil.Category.RDN, attrName);
|
this.x500NameStyle = OidsUtil.attributeNameToOid(OidsUtil.Category.RDN, attrName);
|
||||||
|
log.debug("Attribute Name: " + attrName + " X500NameStyle OID: " + x500NameStyle);
|
||||||
this.x500Name = x500Name;
|
this.x500Name = x500Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +64,7 @@ public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractor
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
Principal name = x500Name.apply(certs);
|
Principal name = x500Name.apply(certs);
|
||||||
|
log.debug("Principal Name " + name.getName());
|
||||||
X500AttributePrincipalDecoder xDecoder = new X500AttributePrincipalDecoder(x500NameStyle);
|
X500AttributePrincipalDecoder xDecoder = new X500AttributePrincipalDecoder(x500NameStyle);
|
||||||
String cn = xDecoder.apply(name);
|
String cn = xDecoder.apply(name);
|
||||||
|
|
||||||
|
@ -95,13 +101,14 @@ public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractor
|
||||||
}
|
}
|
||||||
String subjectName = null;
|
String subjectName = null;
|
||||||
|
|
||||||
logger.info("SubjPrinc " + certs[0].getSubjectX500Principal());
|
log.debug("SubjPrinc " + certs[0].getSubjectX500Principal());
|
||||||
Collection<List<?>> subjectAlternativeNames;
|
Collection<List<?>> subjectAlternativeNames;
|
||||||
try {
|
try {
|
||||||
subjectAlternativeNames = certs[0].getSubjectAlternativeNames();
|
subjectAlternativeNames = certs[0].getSubjectAlternativeNames();
|
||||||
if (subjectAlternativeNames == null) {
|
if (subjectAlternativeNames == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
log.info(Arrays.toString(subjectAlternativeNames.toArray()));
|
||||||
for (List<?> sbjAltName : subjectAlternativeNames) {
|
for (List<?> sbjAltName : subjectAlternativeNames) {
|
||||||
if (sbjAltName == null)
|
if (sbjAltName == null)
|
||||||
continue;
|
continue;
|
||||||
|
@ -109,15 +116,13 @@ public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractor
|
||||||
Integer nameType = (Integer) sbjAltName.get(0);
|
Integer nameType = (Integer) sbjAltName.get(0);
|
||||||
|
|
||||||
if (nameType == generalName) {
|
if (nameType == generalName) {
|
||||||
logger.info("sbjAltName Type " + nameType);
|
|
||||||
logger.info("sbjAltName[1]: " + sbjAltName.get(1));
|
|
||||||
|
|
||||||
Object sbjObj = sbjAltName.get(1);
|
Object sbjObj = sbjAltName.get(1);
|
||||||
|
|
||||||
switch (nameType) {
|
switch (nameType) {
|
||||||
case GeneralName.RFC_822_NAME:
|
case GeneralName.RFC_822_NAME:
|
||||||
case GeneralName.DNS_NAME:
|
case GeneralName.DNS_NAME:
|
||||||
case GeneralName.DIRECTORY_NAME:
|
case GeneralName.DIRECTORY_NAME:
|
||||||
|
case GeneralName.URI_NAME:
|
||||||
subjectName = (String) sbjObj;
|
subjectName = (String) sbjObj;
|
||||||
break;
|
break;
|
||||||
case GeneralName.OTHER_NAME:
|
case GeneralName.OTHER_NAME:
|
||||||
|
@ -126,12 +131,12 @@ public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractor
|
||||||
boolean upnOidFound = false;
|
boolean upnOidFound = false;
|
||||||
while (derDecoder.hasNextElement() && !upnOidFound) {
|
while (derDecoder.hasNextElement() && !upnOidFound) {
|
||||||
int asn1Type = derDecoder.peekType();
|
int asn1Type = derDecoder.peekType();
|
||||||
logger.info("ASN.1 Type: " + derDecoder.peekType());
|
log.debug("ASN.1 Type: " + derDecoder.peekType());
|
||||||
|
|
||||||
switch (asn1Type) {
|
switch (asn1Type) {
|
||||||
case ASN1.OBJECT_IDENTIFIER_TYPE:
|
case ASN1.OBJECT_IDENTIFIER_TYPE:
|
||||||
String oid = derDecoder.decodeObjectIdentifier();
|
String oid = derDecoder.decodeObjectIdentifier();
|
||||||
logger.info("OID: " + oid);
|
log.debug("OID: " + oid);
|
||||||
if(UPN_OID.equals(oid)) {
|
if(UPN_OID.equals(oid)) {
|
||||||
derDecoder.decodeImplicit(160);
|
derDecoder.decodeImplicit(160);
|
||||||
byte[] sb = derDecoder.drainElementValue();
|
byte[] sb = derDecoder.drainElementValue();
|
||||||
|
@ -154,22 +159,28 @@ public class ElytronUserIdentityExtractorProvider extends UserIdentityExtractor
|
||||||
case ASN1.OCTET_STRING_TYPE:
|
case ASN1.OCTET_STRING_TYPE:
|
||||||
subjectName = derDecoder.decodeOctetStringAsString();
|
subjectName = derDecoder.decodeOctetStringAsString();
|
||||||
break;
|
break;
|
||||||
|
case 0xa0:
|
||||||
|
derDecoder.startExplicit(asn1Type);
|
||||||
|
break;
|
||||||
|
case ASN1.SEQUENCE_TYPE:
|
||||||
|
derDecoder.startSequence();
|
||||||
|
default:
|
||||||
|
derDecoder.skipElement();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
logger.info("Subject Alt Name: " + subjectName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (CertificateParsingException | UnsupportedEncodingException e) {
|
} catch (CertificateParsingException | UnsupportedEncodingException e) {
|
||||||
// TODO Auto-generated catch block
|
log.error("Failed to parse Subject Name:",e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug("Subject Alt Name: " + subjectName);
|
||||||
return subjectName;
|
return subjectName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,22 @@ package org.keycloak.crypto.elytron.test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.common.util.PemUtils;
|
||||||
import org.keycloak.crypto.elytron.ElytronCertificateUtils;
|
import org.keycloak.crypto.elytron.ElytronCertificateUtils;
|
||||||
import org.wildfly.security.x500.GeneralName;
|
import org.wildfly.security.x500.GeneralName;
|
||||||
import org.wildfly.security.x500.cert.CRLDistributionPoint;
|
import org.wildfly.security.x500.cert.CRLDistributionPoint;
|
||||||
|
@ -46,9 +50,9 @@ public class CRLDistributionPointTest {
|
||||||
@Test
|
@Test
|
||||||
public void getCrlDistPoint() throws CertificateException, NoSuchAlgorithmException, IOException {
|
public void getCrlDistPoint() throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||||
|
|
||||||
X509Certificate cert = createCRLcert();
|
X509Certificate cert = createCRLcert(1,1);
|
||||||
List<String> expect = new ArrayList<>();
|
List<String> expect = new ArrayList<>();
|
||||||
expect.add("http://crl.test.com");
|
expect.add("http://crl0.test0.com");
|
||||||
|
|
||||||
|
|
||||||
ElytronCertificateUtils bcutil = new ElytronCertificateUtils();
|
ElytronCertificateUtils bcutil = new ElytronCertificateUtils();
|
||||||
|
@ -58,16 +62,67 @@ public class CRLDistributionPointTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private X509Certificate createCRLcert() throws CertificateException, NoSuchAlgorithmException {
|
@Test
|
||||||
|
public void getCrlDistPointMultiNames() throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||||
|
|
||||||
|
X509Certificate cert = createCRLcert(1,2);
|
||||||
|
List<String> expect = new ArrayList<>();
|
||||||
|
expect.add("http://crl0.test0.com");
|
||||||
|
expect.add("http://crl0.test1.com");
|
||||||
|
|
||||||
|
ElytronCertificateUtils bcutil = new ElytronCertificateUtils();
|
||||||
|
List<String> crldp = bcutil.getCRLDistributionPoints(cert);
|
||||||
|
|
||||||
|
assertArrayEquals(expect.toArray(), crldp.toArray());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getMultiCrlDistPointMultiNames() throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||||
|
|
||||||
|
X509Certificate cert = createCRLcert(2,2);
|
||||||
|
List<String> expect = new ArrayList<>();
|
||||||
|
expect.add("http://crl0.test0.com");
|
||||||
|
expect.add("http://crl0.test1.com");
|
||||||
|
expect.add("http://crl1.test0.com");
|
||||||
|
expect.add("http://crl1.test1.com");
|
||||||
|
|
||||||
|
ElytronCertificateUtils bcutil = new ElytronCertificateUtils();
|
||||||
|
List<String> crldp = bcutil.getCRLDistributionPoints(cert);
|
||||||
|
|
||||||
|
assertArrayEquals(expect.toArray(), crldp.toArray());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void revokedCertCRLDistTest() throws CertificateException, IOException {
|
||||||
|
X509Certificate cert = revokedCert();
|
||||||
|
List<String> expect = new ArrayList<>();
|
||||||
|
expect.add("http://localhost:8889/empty.crl");
|
||||||
|
expect.add("http://localhost:8889/intermediate-ca.crl");
|
||||||
|
|
||||||
|
ElytronCertificateUtils bcutil = new ElytronCertificateUtils();
|
||||||
|
List<String> crldp = bcutil.getCRLDistributionPoints(cert);
|
||||||
|
|
||||||
|
assertArrayEquals(expect.toArray(), crldp.toArray());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate createCRLcert(int crldistcount, int namecount) throws CertificateException, NoSuchAlgorithmException {
|
||||||
|
|
||||||
X500Principal dn = new X500Principal("CN=testuser,OU=UNIT,O=TST");
|
X500Principal dn = new X500Principal("CN=testuser,OU=UNIT,O=TST");
|
||||||
List<CRLDistributionPoint> distributionPoints = new ArrayList<>();
|
List<CRLDistributionPoint> distributionPoints = new ArrayList<>();
|
||||||
|
|
||||||
|
for(int x = 0; x<crldistcount;x++) {
|
||||||
List<GeneralName> fullName = new ArrayList<>();
|
List<GeneralName> fullName = new ArrayList<>();
|
||||||
fullName.add(new GeneralName.URIName("http://crl.test.com"));
|
for(int y = 0; y<namecount; y++) {
|
||||||
|
fullName.add(new GeneralName.URIName("http://crl"+x+".test"+y+".com"));
|
||||||
|
}
|
||||||
DistributionPointName distributionPoint = new FullNameDistributionPointName(fullName);
|
DistributionPointName distributionPoint = new FullNameDistributionPointName(fullName);
|
||||||
CRLDistributionPoint arg0 = new CRLDistributionPoint(distributionPoint, null, null);
|
CRLDistributionPoint arg0 = new CRLDistributionPoint(distributionPoint, null, null);
|
||||||
distributionPoints.add(arg0);
|
distributionPoints.add(arg0);
|
||||||
|
|
||||||
|
}
|
||||||
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").genKeyPair();
|
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").genKeyPair();
|
||||||
X509CertificateBuilder cbuilder = new X509CertificateBuilder()
|
X509CertificateBuilder cbuilder = new X509CertificateBuilder()
|
||||||
.setSubjectDn(dn)
|
.setSubjectDn(dn)
|
||||||
|
@ -83,6 +138,16 @@ public class CRLDistributionPointTest {
|
||||||
return cbuilder.build();
|
return cbuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X509Certificate revokedCert() throws CertificateException {
|
||||||
|
String certStr = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"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"
|
||||||
|
+ "\n-----END CERTIFICATE-----";
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
|
||||||
|
return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certStr.getBytes()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,9 +42,13 @@ public class ElytronHmacTest extends HmacTest {
|
||||||
SecureRandom random = isWindows() ? SecureRandom.getInstance("Windows-PRNG") : SecureRandom.getInstance("NativePRNG");
|
SecureRandom random = isWindows() ? SecureRandom.getInstance("Windows-PRNG") : SecureRandom.getInstance("NativePRNG");
|
||||||
random.setSeed(UUID.randomUUID().toString().getBytes());
|
random.setSeed(UUID.randomUUID().toString().getBytes());
|
||||||
keygen.init(random);
|
keygen.init(random);
|
||||||
SecretKey secretKey = keygen.generateKey();
|
SecretKey secret = keygen.generateKey();
|
||||||
|
|
||||||
testHMACSignAndVerify(secretKey, "testHmacSignaturesUsingKeyGen");
|
String encoded = new JWSBuilder().content("12345678901234567890".getBytes())
|
||||||
|
.hmac256(secret);
|
||||||
|
System.out.println("length: " + encoded.length());
|
||||||
|
JWSInput input = new JWSInput(encoded);
|
||||||
|
Assert.assertTrue(HMACProvider.verify(input, secret));
|
||||||
}
|
}
|
||||||
private boolean isWindows(){
|
private boolean isWindows(){
|
||||||
return System.getProperty("os.name").startsWith("Windows");
|
return System.getProperty("os.name").startsWith("Windows");
|
||||||
|
|
|
@ -30,8 +30,6 @@ import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
import org.keycloak.common.util.KeystoreUtil;
|
import org.keycloak.common.util.KeystoreUtil;
|
||||||
import org.keycloak.rule.CryptoInitRule;
|
import org.keycloak.rule.CryptoInitRule;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
|
@ -44,7 +42,7 @@ public class ElytronKeyStoreTypesTest {
|
||||||
@Test
|
@Test
|
||||||
public void testKeystoreFormats() {
|
public void testKeystoreFormats() {
|
||||||
Set<KeystoreUtil.KeystoreFormat> supportedKeystoreFormats = CryptoIntegration.getProvider().getSupportedKeyStoreTypes().collect(Collectors.toSet());
|
Set<KeystoreUtil.KeystoreFormat> supportedKeystoreFormats = CryptoIntegration.getProvider().getSupportedKeyStoreTypes().collect(Collectors.toSet());
|
||||||
assertThat(supportedKeystoreFormats, Matchers.containsInAnyOrder(
|
Assert.assertThat(supportedKeystoreFormats, Matchers.containsInAnyOrder(
|
||||||
KeystoreUtil.KeystoreFormat.JKS,
|
KeystoreUtil.KeystoreFormat.JKS,
|
||||||
KeystoreUtil.KeystoreFormat.PKCS12
|
KeystoreUtil.KeystoreFormat.PKCS12
|
||||||
));
|
));
|
||||||
|
|
|
@ -18,10 +18,14 @@ package org.keycloak.crypto.elytron.test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
|
import org.keycloak.common.crypto.PemUtilsProvider;
|
||||||
import org.keycloak.rule.CryptoInitRule;
|
import org.keycloak.rule.CryptoInitRule;
|
||||||
|
|
||||||
public class ElytronPemUtilsTest {
|
public class ElytronPemUtilsTest {
|
||||||
|
@ -42,5 +46,26 @@ public class ElytronPemUtilsTest {
|
||||||
String encoded = org.keycloak.common.util.PemUtils.generateThumbprint(test, "SHA-256");
|
String encoded = org.keycloak.common.util.PemUtils.generateThumbprint(test, "SHA-256");
|
||||||
assertEquals(43, encoded.length());
|
assertEquals(43, encoded.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testenocdedecode() throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||||
|
PemUtilsProvider pemutil = CryptoIntegration.getProvider().getPemUtils();
|
||||||
|
|
||||||
|
KeyPair keypair = CryptoIntegration.getProvider().getKeyPairGen("RSA").generateKeyPair();
|
||||||
|
String pem = pemutil.encodeKey(keypair.getPrivate());
|
||||||
|
|
||||||
|
Object decodekey = pemutil.decodePrivateKey(pem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testtrkey() {
|
||||||
|
String key = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKtWsK5O0CtuBpnMvWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfdQ2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAECgYB+Y7yBWHIHF2qXGYi6CVvPxtyNBuFcktHYShLyeBNeY3VujYv3QzSZQpJ1zuoXXQuARMHOovyNiVAhu357pMfx9wSkoKNSXKrQx/+9Vt9lI1pXJxjXedPOjbuI/JZAcrk0u4nOfXG/HGtR5cjoDZYWkYQEtsePCnHlZAb0D7axwQJBAO92f00Tvkc9NU/EGqwR3bPXRMqSX0JnG7XRBvLeJBCZYsQn0s2bLdpy8qsTeAyJg1ZvrEc8qIio5HVqzsvbhpMCQQC3K9A6UK+vmQCNWqsQpdqWPRPN7CPB67FzSmyS8CtMjY6jTvSHrkamggotz2N/5QDr1xG2q7A/3dpkq1bTpTx1AkAXZjjiSz+Yrn57IOqKTeSgIjTypoLwdirbBWXsbZCQnqxsBogu1y8P3ZOg6/IbJ4TR+W+YNnExiW9pmdpDSVxJAkEAplTq6YmLf/F4RuQmox94tyUPbtcYQWg942uZ3HSrXQDOng18kBj5nwpHJAJHYEQb6g2K0E5n5hcX0oKkfdx2YQJAcSKAmFiD7KQ6+vVqJlQwVPvYdTSOeZB7YVV6S4b4slS3ZObsa0yNMWgal/QnCtW5k3f185gCWj6dOLGB5btfxg==";
|
||||||
|
|
||||||
|
PemUtilsProvider pemutil = CryptoIntegration.getProvider().getPemUtils();
|
||||||
|
|
||||||
|
pemutil.decodePrivateKey(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package org.keycloak.crypto.elytron.test;
|
||||||
|
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
import java.security.spec.MGF1ParameterSpec;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.PSSParameterSpec;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
|
import org.keycloak.crypto.JavaAlgorithm;
|
||||||
|
import org.keycloak.crypto.KeyWrapper;
|
||||||
|
|
||||||
|
public class ElytronSignatureAlgTest {
|
||||||
|
|
||||||
|
private byte[] data = "Test String to Encrypt".getBytes();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void signatureDefaultAlg() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidAlgorithmParameterException, InvalidKeySpecException {
|
||||||
|
KeyPair keyPair = KeyPairGenerator.getInstance("RSASSA-PSS").genKeyPair();
|
||||||
|
KeyWrapper key = new KeyWrapper();
|
||||||
|
//key.setPrivateKey(keyPair.getPrivate());
|
||||||
|
key.setAlgorithm("PS256");
|
||||||
|
|
||||||
|
KeySpec kspec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
|
||||||
|
key.setPrivateKey(KeyFactory.getInstance("RSASSA-PSS").generatePrivate(kspec));
|
||||||
|
|
||||||
|
Signature signature = Signature.getInstance("RSASSA-PSS");
|
||||||
|
MGF1ParameterSpec ps = MGF1ParameterSpec.SHA256;
|
||||||
|
AlgorithmParameterSpec params = new PSSParameterSpec(ps.getDigestAlgorithm(), "MGF1", ps, 32, 1);
|
||||||
|
|
||||||
|
signature.setParameter(params);
|
||||||
|
signature.initSign(keyPair.getPrivate());
|
||||||
|
//signature.initSign((PrivateKey) key.getPrivateKey());
|
||||||
|
signature.update(data);
|
||||||
|
System.out.println(signature.getProvider() + " Alg ###########");
|
||||||
|
if(signature.getAlgorithm().equals("RSASSA-PSS")) {
|
||||||
|
}
|
||||||
|
signature.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue