diff --git a/jans-auth-server/client/pom.xml b/jans-auth-server/client/pom.xml index 14a8f49a6c9..3deea71d6a7 100644 --- a/jans-auth-server/client/pom.xml +++ b/jans-auth-server/client/pom.xml @@ -237,11 +237,11 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on org.testng diff --git a/jans-auth-server/common/pom.xml b/jans-auth-server/common/pom.xml index c050308b70f..ce88f413208 100644 --- a/jans-auth-server/common/pom.xml +++ b/jans-auth-server/common/pom.xml @@ -163,11 +163,11 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on com.nimbusds diff --git a/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/CRLCertificateVerifier.java b/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/CRLCertificateVerifier.java index d2fb0a66079..22e1e038685 100644 --- a/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/CRLCertificateVerifier.java +++ b/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/CRLCertificateVerifier.java @@ -6,36 +6,12 @@ package io.jans.as.common.cert.validation; -import com.google.common.base.Preconditions; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import io.jans.as.common.cert.validation.model.ValidationStatus; -import io.jans.util.security.SecurityProviderUtility; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.input.BoundedInputStream; -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1TaggedObject; -import org.bouncycastle.asn1.DERIA5String; -import org.bouncycastle.asn1.x509.CRLDistPoint; -import org.bouncycastle.asn1.x509.DistributionPoint; -import org.bouncycastle.asn1.x509.DistributionPointName; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.GeneralName; -import org.bouncycastle.asn1.x509.GeneralNames; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.security.NoSuchProviderException; import java.security.Principal; @@ -50,6 +26,37 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.BoundedInputStream; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.X509Extensions; +import org.bouncycastle.x509.NoSuchParserException; +import org.bouncycastle.x509.util.StreamParsingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import io.jans.as.common.cert.validation.model.ValidationStatus; +import io.jans.as.common.cert.validation.model.ValidationStatus.CertificateValidity; +import io.jans.as.common.cert.validation.model.ValidationStatus.ValidatorSourceType; +import io.jans.as.model.util.CertUtils; +import io.jans.util.security.SecurityProviderUtility; + /** * Certificate verifier based on CRL * @@ -58,231 +65,239 @@ */ public class CRLCertificateVerifier implements CertificateVerifier { - private static final Logger log = LoggerFactory.getLogger(CRLCertificateVerifier.class); - - private final int maxCrlSize; - - private final LoadingCache crlCache; - - public CRLCertificateVerifier(final int maxCrlSize) { - SecurityProviderUtility.installBCProvider(true); - - this.maxCrlSize = maxCrlSize; - - CacheLoader checkedLoader = new CacheLoader() { - public X509CRL load(String crlURL) throws CertificateException, CRLException, NoSuchProviderException, IOException, ExecutionException { - X509CRL result = requestCRL(crlURL); - Preconditions.checkNotNull(result); - - return result; - } - }; - - this.crlCache = CacheBuilder.newBuilder().maximumSize(10).expireAfterWrite(60, TimeUnit.MINUTES).build(checkedLoader); - } - - /** - * @param certificate the certificate from which we need the ExtensionValue - * @param oid the Object Identifier value for the extension. - * @return the extension value as an ASN1Primitive object - * @throws IOException - */ - private static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException { - byte[] bytes = certificate.getExtensionValue(oid); - if (bytes == null) { - return null; - } - ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); - ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); - aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); - return aIn.readObject(); - } - - @Override - public ValidationStatus validate(X509Certificate certificate, List issuers, Date validationDate) { - X509Certificate issuer = issuers.get(0); - ValidationStatus status = new ValidationStatus(certificate, issuer, validationDate, ValidationStatus.ValidatorSourceType.CRL, ValidationStatus.CertificateValidity.UNKNOWN); - - try { - Principal subjectX500Principal = certificate.getSubjectX500Principal(); - - String crlURL = getCrlUri(certificate); - if (crlURL == null) { - log.error("CRL's URL for '" + subjectX500Principal + "' is empty"); - return status; - } - - log.debug("CRL's URL for '" + subjectX500Principal + "' is '" + crlURL + "'"); - - X509CRL x509crl = getCrl(crlURL); - if (!validateCRL(x509crl, certificate, issuer, validationDate)) { - log.error("The CRL is not valid!"); - status.setValidity(ValidationStatus.CertificateValidity.INVALID); - return status; - } - - X509CRLEntry crlEntry = x509crl.getRevokedCertificate(certificate.getSerialNumber()); - if (crlEntry == null) { - log.debug("CRL status is valid for '" + subjectX500Principal + "'"); - status.setValidity(ValidationStatus.CertificateValidity.VALID); - } else if (crlEntry.getRevocationDate().after(validationDate)) { - log.warn("CRL revocation time after the validation date, the certificate '" + subjectX500Principal + "' was valid at " + validationDate); - status.setRevocationObjectIssuingTime(x509crl.getThisUpdate()); - status.setValidity(ValidationStatus.CertificateValidity.VALID); - } else { - log.info("CRL for certificate '" + subjectX500Principal + "' is revoked since " + crlEntry.getRevocationDate()); - status.setRevocationObjectIssuingTime(x509crl.getThisUpdate()); - status.setRevocationDate(crlEntry.getRevocationDate()); - status.setValidity(ValidationStatus.CertificateValidity.REVOKED); - } - } catch (Exception ex) { - log.error("CRL exception: ", ex); - } - - return status; - } - - private boolean validateCRL(X509CRL x509crl, X509Certificate certificate, X509Certificate issuerCertificate, Date validationDate) { - Principal subjectX500Principal = certificate.getSubjectX500Principal(); - - if (x509crl == null) { - log.error("No CRL found for certificate '" + subjectX500Principal + "'"); - return false; - } - - if (log.isTraceEnabled()) { - try { - log.trace("CRL number: " + getCrlNumber(x509crl)); - } catch (IOException ex) { - log.error("Failed to get CRL number", ex); - } - } - - if (!x509crl.getIssuerX500Principal().equals(issuerCertificate.getSubjectX500Principal())) { - log.error("The CRL must be signed by the issuer '" + subjectX500Principal + "' but instead is signed by '" - + x509crl.getIssuerX500Principal() + "'"); - return false; - } - - try { - x509crl.verify(issuerCertificate.getPublicKey()); - } catch (Exception ex) { - log.error("The signature verification for CRL cannot be performed", ex); - return false; - } - - log.debug("CRL validationDate: " + validationDate); - log.debug("CRL nextUpdate: " + x509crl.getThisUpdate()); - log.debug("CRL thisUpdate: " + x509crl.getNextUpdate()); - - if (x509crl.getNextUpdate() != null && validationDate.after(x509crl.getNextUpdate())) { - log.error("CRL is too old"); - return false; - } - - if (issuerCertificate.getKeyUsage() == null) { - log.error("There is no KeyUsage extension for certificate '" + subjectX500Principal + "'"); - return false; - } - - if (!issuerCertificate.getKeyUsage()[6]) { - log.error("cRLSign bit is not set for CRL certificate'" + subjectX500Principal + "'"); - return false; - } - - return true; - - } - - private X509CRL getCrl(String url) throws ExecutionException { - if (!(url.startsWith("http://") || url.startsWith("https://"))) { - log.error("It's possible to download CRL via HTTP and HTTPS only"); - return null; - } - - String cacheKey = url.toLowerCase(); - return crlCache.get(cacheKey); - } - - public X509CRL requestCRL(String url) throws IOException, CertificateException, CRLException { - HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); - try { - con.setUseCaches(false); - - InputStream in = new BoundedInputStream(con.getInputStream(), maxCrlSize); - try { - CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - X509CRL crl = (X509CRL) certificateFactory.generateCRL(in); - log.debug("CRL size: " + crl.getEncoded().length + " bytes"); - - return crl; - } finally { - IOUtils.closeQuietly(in); - } - } catch (IOException ex) { - log.error("Failed to download CRL from '" + url + "'", ex); - } finally { - if (con != null) { - con.disconnect(); - } - } - - return null; - } - - @SuppressWarnings({"deprecation", "resource"}) - private BigInteger getCrlNumber(X509CRL crl) throws IOException { - byte[] crlNumberExtensionValue = crl.getExtensionValue(Extension.cRLNumber.getId()); - if (crlNumberExtensionValue == null) { - return null; - } - - ASN1OctetString octetString = (ASN1OctetString) (new ASN1InputStream(new ByteArrayInputStream(crlNumberExtensionValue)).readObject()); - byte[] octets = octetString.getOctets(); - ASN1Integer integer = (ASN1Integer) new ASN1InputStream(octets).readObject(); - return integer.getPositiveValue(); - } - - public String getCrlUri(X509Certificate certificate) throws IOException { - ASN1Primitive obj; - try { - obj = getExtensionValue(certificate, Extension.cRLDistributionPoints.getId()); - } catch (IOException ex) { - log.error("Failed to get CRL URL", ex); - return null; - } - - if (obj == null) { - return null; - } - - CRLDistPoint distPoint = CRLDistPoint.getInstance(obj); - - DistributionPoint[] distributionPoints = distPoint.getDistributionPoints(); - for (DistributionPoint distributionPoint : distributionPoints) { - DistributionPointName distributionPointName = distributionPoint.getDistributionPoint(); - if (DistributionPointName.FULL_NAME != distributionPointName.getType()) { - continue; - } - - GeneralNames generalNames = (GeneralNames) distributionPointName.getName(); - GeneralName[] names = generalNames.getNames(); - for (GeneralName name : names) { - if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { - continue; - } - - DERIA5String derStr = DERIA5String.getInstance((ASN1TaggedObject) name.toASN1Primitive(), false); - return derStr.getString(); - } - } - - return null; - } - - @Override - public void destroy() { - crlCache.cleanUp(); - } + private static final Logger log = LoggerFactory.getLogger(CRLCertificateVerifier.class); + + private int maxCrlSize; + + private LoadingCache crlCache; + + public CRLCertificateVerifier(final int maxCrlSize) { + SecurityProviderUtility.installBCProvider(true); + + this.maxCrlSize = maxCrlSize; + + CacheLoader checkedLoader = new CacheLoader() { + public X509CRL load(String crlURL) throws CertificateException, CRLException, NoSuchProviderException, NoSuchParserException, StreamParsingException, MalformedURLException, IOException, ExecutionException { + X509CRL result = requestCRL(crlURL); + Preconditions.checkNotNull(result); + + return result; + } + }; + + this.crlCache = CacheBuilder.newBuilder().maximumSize(10).expireAfterWrite(60, TimeUnit.MINUTES).build(checkedLoader); + } + + @Override + public ValidationStatus validate(X509Certificate certificate, List issuers, Date validationDate) { + + X509Certificate issuer = CertUtils.getIssuer(certificate, issuers); + ValidationStatus status = new ValidationStatus(certificate, issuer, validationDate, ValidatorSourceType.CRL, CertificateValidity.UNKNOWN); + + try { + Principal subjectX500Principal = certificate.getSubjectX500Principal(); + + String crlURL = getCrlUri(certificate); + if (crlURL == null) { + log.error("CRL's URL for '" + subjectX500Principal + "' is empty"); + return status; + } + + log.debug("CRL's URL for '" + subjectX500Principal + "' is '" + crlURL + "'"); + + X509CRL x509crl = getCrl(crlURL); + if (!validateCRL(x509crl, certificate, issuer, validationDate)) { + log.error("The CRL is not valid!"); + status.setValidity(CertificateValidity.INVALID); + return status; + } + + X509CRLEntry crlEntry = x509crl.getRevokedCertificate(certificate.getSerialNumber()); + if (crlEntry == null) { + log.debug("CRL status is valid for '" + subjectX500Principal + "'"); + status.setValidity(CertificateValidity.VALID); + } else if (crlEntry.getRevocationDate().after(validationDate)) { + log.warn("CRL revocation time after the validation date, the certificate '" + subjectX500Principal + "' was valid at " + validationDate); + status.setRevocationObjectIssuingTime(x509crl.getThisUpdate()); + status.setValidity(CertificateValidity.VALID); + } else { + log.info("CRL for certificate '" + subjectX500Principal + "' is revoked since " + crlEntry.getRevocationDate()); + status.setRevocationObjectIssuingTime(x509crl.getThisUpdate()); + status.setRevocationDate(crlEntry.getRevocationDate()); + status.setValidity(CertificateValidity.REVOKED); + } + } catch (Exception ex) { + log.error("CRL exception: ", ex); + } + + return status; + } + + private boolean validateCRL(X509CRL x509crl, X509Certificate certificate, X509Certificate issuerCertificate, Date validationDate) { + Principal subjectX500Principal = certificate.getSubjectX500Principal(); + + if (x509crl == null) { + log.error("No CRL found for certificate '" + subjectX500Principal + "'"); + return false; + } + + if (log.isTraceEnabled()) { + try { + log.trace("CRL number: " + getCrlNumber(x509crl)); + } catch (IOException ex) { + log.error("Failed to get CRL number", ex); + } + } + + if (!x509crl.getIssuerX500Principal().equals(issuerCertificate.getSubjectX500Principal())) { + log.error("The CRL must be signed by the issuer '" + subjectX500Principal + "' but instead is signed by '" + + x509crl.getIssuerX500Principal() + "'"); + return false; + } + + try { + x509crl.verify(issuerCertificate.getPublicKey()); + } catch (Exception ex) { + log.error("The signature verification for CRL cannot be performed", ex); + return false; + } + + log.debug("CRL validationDate: " + validationDate); + log.debug("CRL nextUpdate: " + x509crl.getThisUpdate()); + log.debug("CRL thisUpdate: " + x509crl.getNextUpdate()); + + if (x509crl.getNextUpdate() != null && validationDate.after(x509crl.getNextUpdate())) { + log.error("CRL is too old"); + return false; + } + + if (issuerCertificate.getKeyUsage() == null) { + log.error("There is no KeyUsage extension for certificate '" + subjectX500Principal + "'"); + return false; + } + + if (!issuerCertificate.getKeyUsage()[6]) { + log.error("cRLSign bit is not set for CRL certificate'" + subjectX500Principal + "'"); + return false; + } + + return true; + + } + + private X509CRL getCrl(String url) throws CertificateException, CRLException, NoSuchProviderException, NoSuchParserException, StreamParsingException, + MalformedURLException, IOException, ExecutionException { + if (!(url.startsWith("http://") || url.startsWith("https://"))) { + log.error("It's possible to download CRL via HTTP and HTTPS only"); + return null; + } + + String cacheKey = url.toLowerCase(); + X509CRL crl = crlCache.get(cacheKey); + + return crl; + } + + public X509CRL requestCRL(String url) throws IOException, MalformedURLException, CertificateException, CRLException { + HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); + try { + con.setUseCaches(false); + + InputStream in = new BoundedInputStream(con.getInputStream(), maxCrlSize); + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL) certificateFactory.generateCRL(in); + log.debug("CRL size: " + crl.getEncoded().length + " bytes"); + + return crl; + } finally { + IOUtils.closeQuietly(in); + } + } catch (IOException ex) { + log.error("Failed to download CRL from '" + url + "'", ex); + } finally { + if (con != null) { + con.disconnect(); + } + } + + return null; + } + + @SuppressWarnings({ "deprecation", "resource" }) + private BigInteger getCrlNumber(X509CRL crl) throws IOException { + byte[] crlNumberExtensionValue = crl.getExtensionValue(X509Extensions.CRLNumber.getId()); + if (crlNumberExtensionValue == null) { + return null; + } + + ASN1OctetString octetString = (ASN1OctetString) (new ASN1InputStream(new ByteArrayInputStream(crlNumberExtensionValue)).readObject()); + byte[] octets = octetString.getOctets(); + ASN1Integer integer = (ASN1Integer) new ASN1InputStream(octets).readObject(); + BigInteger crlNumber = integer.getPositiveValue(); + + return crlNumber; + } + + public String getCrlUri(X509Certificate certificate) throws IOException { + ASN1Primitive obj; + try { + obj = getExtensionValue(certificate, Extension.cRLDistributionPoints.getId()); + } catch (IOException ex) { + log.error("Failed to get CRL URL", ex); + return null; + } + + if (obj == null) { + return null; + } + + CRLDistPoint distPoint = CRLDistPoint.getInstance(obj); + + DistributionPoint[] distributionPoints = distPoint.getDistributionPoints(); + for (DistributionPoint distributionPoint : distributionPoints) { + DistributionPointName distributionPointName = distributionPoint.getDistributionPoint(); + if (DistributionPointName.FULL_NAME != distributionPointName.getType()) { + continue; + } + + GeneralNames generalNames = (GeneralNames) distributionPointName.getName(); + GeneralName[] names = generalNames.getNames(); + for (GeneralName name : names) { + if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { + continue; + } + + DERIA5String derStr = (DERIA5String) DERIA5String.getInstance((DERTaggedObject) name.toASN1Primitive(), false); + return derStr.getString(); + } + } + + return null; + } + + /** + * @param certificate + * the certificate from which we need the ExtensionValue + * @param oid + * the Object Identifier value for the extension. + * @return the extension value as an ASN1Primitive object + * @throws IOException + */ + private static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException { + byte[] bytes = certificate.getExtensionValue(oid); + if (bytes == null) { + return null; + } + ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); + ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); + aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); + return aIn.readObject(); + } + + @Override + public void destroy() { + crlCache.cleanUp(); + } } diff --git a/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/OCSPCertificateVerifier.java b/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/OCSPCertificateVerifier.java index 8c844e021a6..3ea7e22f67c 100644 --- a/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/OCSPCertificateVerifier.java +++ b/jans-auth-server/common/src/main/java/io/jans/as/common/cert/validation/OCSPCertificateVerifier.java @@ -22,8 +22,8 @@ import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.AccessDescription; import org.bouncycastle.asn1.x509.AuthorityInformationAccess; import org.bouncycastle.asn1.x509.Extension; @@ -60,188 +60,188 @@ */ public class OCSPCertificateVerifier implements CertificateVerifier { - private static final Logger log = LoggerFactory.getLogger(OCSPCertificateVerifier.class); - - public OCSPCertificateVerifier() { - SecurityProviderUtility.installBCProvider(true); - } - - /** - * @param certificate - * the certificate from which we need the ExtensionValue - * @param oid - * the Object Identifier value for the extension. - * @return the extension value as an ASN1Primitive object - * @throws IOException - */ - private static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException { - byte[] bytes = certificate.getExtensionValue(oid); - if (bytes == null) { - return null; - } - ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); - ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); - aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); - return aIn.readObject(); - } - - @Override - public ValidationStatus validate(X509Certificate certificate, List issuers, Date validationDate) { - - X509Certificate issuer = CertUtils.getIssuer(certificate, issuers); - ValidationStatus status = new ValidationStatus(certificate, issuer, validationDate, ValidatorSourceType.OCSP, CertificateValidity.UNKNOWN); - - try { - Principal subjectX500Principal = certificate.getSubjectX500Principal(); - - String ocspUrl = getOCSPUrl(certificate); - if (ocspUrl == null) { - log.error("OCSP URL for '" + subjectX500Principal + "' is empty"); - return status; - } - - log.debug("OCSP URL for '" + subjectX500Principal + "' is '" + ocspUrl + "'"); - - DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1); - CertificateID certificateId = new CertificateID(digestCalculator, new JcaX509CertificateHolder(issuer), certificate.getSerialNumber()); - - // Generate OCSP request - OCSPReq ocspReq = generateOCSPRequest(certificateId); - - // Get OCSP response from server - OCSPResp ocspResp = requestOCSPResponse(ocspUrl, ocspReq); - if (ocspResp.getStatus() != OCSPRespBuilder.SUCCESSFUL) { - log.error("OCSP response is invalid!"); - status.setValidity(CertificateValidity.INVALID); - return status; - } - - boolean foundResponse = false; - BasicOCSPResp basicOCSPResp = (BasicOCSPResp) ocspResp.getResponseObject(); - SingleResp[] singleResps = basicOCSPResp.getResponses(); - for (SingleResp singleResp : singleResps) { - CertificateID responseCertificateId = singleResp.getCertID(); - if (!certificateId.equals(responseCertificateId)) { - continue; - } - - foundResponse = true; - - log.debug("OCSP validationDate: " + validationDate); - log.debug("OCSP thisUpdate: " + singleResp.getThisUpdate()); - log.debug("OCSP nextUpdate: " + singleResp.getNextUpdate()); - - status.setRevocationObjectIssuingTime(basicOCSPResp.getProducedAt()); - - Object certStatus = singleResp.getCertStatus(); - if (certStatus == CertificateStatus.GOOD) { - log.debug("OCSP status is valid for '" + certificate.getSubjectX500Principal() + "'"); - status.setValidity(CertificateValidity.VALID); - } else { - if (singleResp.getCertStatus() instanceof RevokedStatus) { - log.warn("OCSP status is revoked for: " + subjectX500Principal); - if (validationDate.before(((RevokedStatus) singleResp.getCertStatus()).getRevocationTime())) { - log.warn("OCSP revocation time after the validation date, the certificate '" + subjectX500Principal + "' was valid at " + validationDate); - status.setValidity(CertificateValidity.VALID); - } else { - Date revocationDate = ((RevokedStatus) singleResp.getCertStatus()).getRevocationTime(); - log.info("OCSP for certificate '" + subjectX500Principal + "' is revoked since " + revocationDate); - status.setRevocationDate(revocationDate); - status.setRevocationObjectIssuingTime(singleResp.getThisUpdate()); - status.setValidity(CertificateValidity.REVOKED); - } - } - } - } - - if (!foundResponse) { - log.error("There is no matching OCSP response entries"); - } - } catch (Exception ex) { - log.error("OCSP exception: ", ex); - } - - return status; - } - - private OCSPReq generateOCSPRequest(CertificateID certificateId) throws OCSPException, OperatorCreationException, CertificateEncodingException { - OCSPReqBuilder ocspReqGenerator = new OCSPReqBuilder(); - - ocspReqGenerator.addRequest(certificateId); - - OCSPReq ocspReq = ocspReqGenerator.build(); - return ocspReq; - } - - @SuppressWarnings({ "deprecation", "resource" }) - private String getOCSPUrl(X509Certificate certificate) throws IOException { - ASN1Primitive obj; - try { - obj = getExtensionValue(certificate, Extension.authorityInfoAccess.getId()); - } catch (IOException ex) { - log.error("Failed to get OCSP URL", ex); - return null; - } - - if (obj == null) { - return null; - } - - AuthorityInformationAccess authorityInformationAccess = AuthorityInformationAccess.getInstance(obj); - - AccessDescription[] accessDescriptions = authorityInformationAccess.getAccessDescriptions(); - for (AccessDescription accessDescription : accessDescriptions) { - boolean correctAccessMethod = accessDescription.getAccessMethod().equals((Object)X509ObjectIdentifiers.ocspAccessMethod); - if (!correctAccessMethod) { - continue; - } - - GeneralName name = accessDescription.getAccessLocation(); - if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { - continue; - } - - DERIA5String derStr = DERIA5String.getInstance((ASN1TaggedObject) name.toASN1Primitive(), false); - return derStr.getString(); - } - - return null; - - } - - public OCSPResp requestOCSPResponse(String url, OCSPReq ocspReq) throws IOException, MalformedURLException { - byte[] ocspReqData = ocspReq.getEncoded(); - - HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); - try { - con.setRequestProperty("Content-Type", "application/ocsp-request"); - con.setRequestProperty("Accept", "application/ocsp-response"); - - con.setDoInput(true); - con.setDoOutput(true); - con.setUseCaches(false); - - OutputStream out = con.getOutputStream(); - try { - IOUtils.write(ocspReqData, out); - out.flush(); - } finally { - IOUtils.closeQuietly(out); - } - - byte[] responseBytes = IOUtils.toByteArray(con.getInputStream()); - OCSPResp ocspResp = new OCSPResp(responseBytes); - - return ocspResp; - } finally { - if (con != null) { - con.disconnect(); - } - } - } - - @Override - public void destroy() { - } + private static final Logger log = LoggerFactory.getLogger(OCSPCertificateVerifier.class); + + public OCSPCertificateVerifier() { + SecurityProviderUtility.installBCProvider(true); + } + + @Override + public ValidationStatus validate(X509Certificate certificate, List issuers, Date validationDate) { + + X509Certificate issuer = CertUtils.getIssuer(certificate, issuers); + ValidationStatus status = new ValidationStatus(certificate, issuer, validationDate, ValidatorSourceType.OCSP, CertificateValidity.UNKNOWN); + + try { + Principal subjectX500Principal = certificate.getSubjectX500Principal(); + + String ocspUrl = getOCSPUrl(certificate); + if (ocspUrl == null) { + log.error("OCSP URL for '" + subjectX500Principal + "' is empty"); + return status; + } + + log.debug("OCSP URL for '" + subjectX500Principal + "' is '" + ocspUrl + "'"); + + DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1); + CertificateID certificateId = new CertificateID(digestCalculator, new JcaX509CertificateHolder(issuer), certificate.getSerialNumber()); + + // Generate OCSP request + OCSPReq ocspReq = generateOCSPRequest(certificateId); + + // Get OCSP response from server + OCSPResp ocspResp = requestOCSPResponse(ocspUrl, ocspReq); + if (ocspResp.getStatus() != OCSPRespBuilder.SUCCESSFUL) { + log.error("OCSP response is invalid!"); + status.setValidity(CertificateValidity.INVALID); + return status; + } + + boolean foundResponse = false; + BasicOCSPResp basicOCSPResp = (BasicOCSPResp) ocspResp.getResponseObject(); + SingleResp[] singleResps = basicOCSPResp.getResponses(); + for (SingleResp singleResp : singleResps) { + CertificateID responseCertificateId = singleResp.getCertID(); + if (!certificateId.equals(responseCertificateId)) { + continue; + } + + foundResponse = true; + + log.debug("OCSP validationDate: " + validationDate); + log.debug("OCSP thisUpdate: " + singleResp.getThisUpdate()); + log.debug("OCSP nextUpdate: " + singleResp.getNextUpdate()); + + status.setRevocationObjectIssuingTime(basicOCSPResp.getProducedAt()); + + Object certStatus = singleResp.getCertStatus(); + if (certStatus == CertificateStatus.GOOD) { + log.debug("OCSP status is valid for '" + certificate.getSubjectX500Principal() + "'"); + status.setValidity(CertificateValidity.VALID); + } else { + if (singleResp.getCertStatus() instanceof RevokedStatus) { + log.warn("OCSP status is revoked for: " + subjectX500Principal); + if (validationDate.before(((RevokedStatus) singleResp.getCertStatus()).getRevocationTime())) { + log.warn("OCSP revocation time after the validation date, the certificate '" + subjectX500Principal + "' was valid at " + validationDate); + status.setValidity(CertificateValidity.VALID); + } else { + Date revocationDate = ((RevokedStatus) singleResp.getCertStatus()).getRevocationTime(); + log.info("OCSP for certificate '" + subjectX500Principal + "' is revoked since " + revocationDate); + status.setRevocationDate(revocationDate); + status.setRevocationObjectIssuingTime(singleResp.getThisUpdate()); + status.setValidity(CertificateValidity.REVOKED); + } + } + } + } + + if (!foundResponse) { + log.error("There is no matching OCSP response entries"); + } + } catch (Exception ex) { + log.error("OCSP exception: ", ex); + } + + return status; + } + + private OCSPReq generateOCSPRequest(CertificateID certificateId) throws OCSPException, OperatorCreationException, CertificateEncodingException { + OCSPReqBuilder ocspReqGenerator = new OCSPReqBuilder(); + + ocspReqGenerator.addRequest(certificateId); + + OCSPReq ocspReq = ocspReqGenerator.build(); + return ocspReq; + } + + @SuppressWarnings({ "deprecation", "resource" }) + private String getOCSPUrl(X509Certificate certificate) throws IOException { + ASN1Primitive obj; + try { + obj = getExtensionValue(certificate, Extension.authorityInfoAccess.getId()); + } catch (IOException ex) { + log.error("Failed to get OCSP URL", ex); + return null; + } + + if (obj == null) { + return null; + } + + AuthorityInformationAccess authorityInformationAccess = AuthorityInformationAccess.getInstance(obj); + + AccessDescription[] accessDescriptions = authorityInformationAccess.getAccessDescriptions(); + for (AccessDescription accessDescription : accessDescriptions) { + boolean correctAccessMethod = accessDescription.getAccessMethod().equals((Object)X509ObjectIdentifiers.ocspAccessMethod); + if (!correctAccessMethod) { + continue; + } + + GeneralName name = accessDescription.getAccessLocation(); + if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { + continue; + } + + DERIA5String derStr = (DERIA5String) DERIA5String.getInstance((DERTaggedObject) name.toASN1Primitive(), false); + return derStr.getString(); + } + + return null; + + } + + public OCSPResp requestOCSPResponse(String url, OCSPReq ocspReq) throws IOException, MalformedURLException { + byte[] ocspReqData = ocspReq.getEncoded(); + + HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); + try { + con.setRequestProperty("Content-Type", "application/ocsp-request"); + con.setRequestProperty("Accept", "application/ocsp-response"); + + con.setDoInput(true); + con.setDoOutput(true); + con.setUseCaches(false); + + OutputStream out = con.getOutputStream(); + try { + IOUtils.write(ocspReqData, out); + out.flush(); + } finally { + IOUtils.closeQuietly(out); + } + + byte[] responseBytes = IOUtils.toByteArray(con.getInputStream()); + OCSPResp ocspResp = new OCSPResp(responseBytes); + + return ocspResp; + } finally { + if (con != null) { + con.disconnect(); + } + } + } + + /** + * @param certificate + * the certificate from which we need the ExtensionValue + * @param oid + * the Object Identifier value for the extension. + * @return the extension value as an ASN1Primitive object + * @throws IOException + */ + private static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException { + byte[] bytes = certificate.getExtensionValue(oid); + if (bytes == null) { + return null; + } + ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); + ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); + aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); + return aIn.readObject(); + } + + @Override + public void destroy() { + } } diff --git a/jans-auth-server/model/pom.xml b/jans-auth-server/model/pom.xml index f24aa5ca6b2..af7793f4263 100644 --- a/jans-auth-server/model/pom.xml +++ b/jans-auth-server/model/pom.xml @@ -140,12 +140,12 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on provided org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on provided diff --git a/jans-auth-server/server-fips/pom.xml b/jans-auth-server/server-fips/pom.xml index 3c4426db36f..df696e38163 100644 --- a/jans-auth-server/server-fips/pom.xml +++ b/jans-auth-server/server-fips/pom.xml @@ -87,10 +87,10 @@ - WEB-INF/lib/bcpkix-jdk15on-*.jar, - WEB-INF/lib/bcprov-jdk15on-*.jar, - WEB-INF/lib/bcutil-jdk15on-*.jar, - WEB-INF/lib/bcmail-jdk15on-*.jar + WEB-INF/lib/bcpkix-jdk18on-*.jar, + WEB-INF/lib/bcprov-jdk18on-*.jar, + WEB-INF/lib/bcutil-jdk18on-*.jar, + WEB-INF/lib/bcmail-jdk18on-*.jar diff --git a/jans-auth-server/server/pom.xml b/jans-auth-server/server/pom.xml index 71bdaa01ab3..2ca1612627f 100644 --- a/jans-auth-server/server/pom.xml +++ b/jans-auth-server/server/pom.xml @@ -459,11 +459,11 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on diff --git a/jans-auth-server/test-model/pom.xml b/jans-auth-server/test-model/pom.xml index 9cf702066f4..d485a74ddc7 100644 --- a/jans-auth-server/test-model/pom.xml +++ b/jans-auth-server/test-model/pom.xml @@ -123,12 +123,12 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on provided org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on provided diff --git a/jans-bom/pom.xml b/jans-bom/pom.xml index c455cb26130..690a888d8fb 100644 --- a/jans-bom/pom.xml +++ b/jans-bom/pom.xml @@ -56,7 +56,7 @@ 1.12.448 2.21.13 - 1.70 + 1.78 5.9.2 5.1.1 @@ -276,17 +276,17 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on ${bc.version} org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on ${bc.version} org.bouncycastle - bcmail-jdk15on + bcmail-jdk18on ${bc.version} diff --git a/jans-casa/app/pom.xml b/jans-casa/app/pom.xml index 2876a98870e..777467b6fd0 100644 --- a/jans-casa/app/pom.xml +++ b/jans-casa/app/pom.xml @@ -337,10 +337,10 @@ - WEB-INF/lib/bcpkix-jdk15on-*.jar, - WEB-INF/lib/bcprov-jdk15on-*.jar, - WEB-INF/lib/bcutil-jdk15on-*.jar, - WEB-INF/lib/bcmail-jdk15on-*.jar + WEB-INF/lib/bcpkix-jdk18on-*.jar, + WEB-INF/lib/bcprov-jdk18on-*.jar, + WEB-INF/lib/bcutil-jdk18on-*.jar, + WEB-INF/lib/bcmail-jdk18on-*.jar diff --git a/jans-config-api/server-fips/pom.xml b/jans-config-api/server-fips/pom.xml index b498b368962..bebc4b2c4c0 100644 --- a/jans-config-api/server-fips/pom.xml +++ b/jans-config-api/server-fips/pom.xml @@ -87,10 +87,10 @@ - WEB-INF/lib/bcpkix-jdk15on-*.jar, - WEB-INF/lib/bcprov-jdk15on-*.jar, - WEB-INF/lib/bcutil-jdk15on-*.jar, - WEB-INF/lib/bcmail-jdk15on-*.jar + WEB-INF/lib/bcpkix-jdk18on-*.jar, + WEB-INF/lib/bcprov-jdk18on-*.jar, + WEB-INF/lib/bcutil-jdk18on-*.jar, + WEB-INF/lib/bcmail-jdk18on-*.jar diff --git a/jans-core/service/pom.xml b/jans-core/service/pom.xml index afb0ee51e4e..49ae4dbcd11 100644 --- a/jans-core/service/pom.xml +++ b/jans-core/service/pom.xml @@ -183,15 +183,15 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on org.bouncycastle - bcmail-jdk15on + bcmail-jdk18on diff --git a/jans-fido2/client/pom.xml b/jans-fido2/client/pom.xml index 4547bd27ea8..768a22a0006 100644 --- a/jans-fido2/client/pom.xml +++ b/jans-fido2/client/pom.xml @@ -93,7 +93,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on diff --git a/jans-fido2/server-fips/pom.xml b/jans-fido2/server-fips/pom.xml index e11b450ce47..641466efab9 100644 --- a/jans-fido2/server-fips/pom.xml +++ b/jans-fido2/server-fips/pom.xml @@ -87,10 +87,10 @@ - WEB-INF/lib/bcpkix-jdk15on-*.jar, - WEB-INF/lib/bcprov-jdk15on-*.jar, - WEB-INF/lib/bcutil-jdk15on-*.jar, - WEB-INF/lib/bcmail-jdk15on-*.jar + WEB-INF/lib/bcpkix-jdk18on-*.jar, + WEB-INF/lib/bcprov-jdk18on-*.jar, + WEB-INF/lib/bcutil-jdk18on-*.jar, + WEB-INF/lib/bcmail-jdk18on-*.jar diff --git a/jans-link/server-fips/pom.xml b/jans-link/server-fips/pom.xml index 0c06d802cb7..4f96ae35af4 100644 --- a/jans-link/server-fips/pom.xml +++ b/jans-link/server-fips/pom.xml @@ -87,10 +87,10 @@ - WEB-INF/lib/bcpkix-jdk15on-*.jar, - WEB-INF/lib/bcprov-jdk15on-*.jar, - WEB-INF/lib/bcutil-jdk15on-*.jar, - WEB-INF/lib/bcmail-jdk15on-*.jar + WEB-INF/lib/bcpkix-jdk18on-*.jar, + WEB-INF/lib/bcprov-jdk18on-*.jar, + WEB-INF/lib/bcutil-jdk18on-*.jar, + WEB-INF/lib/bcmail-jdk18on-*.jar diff --git a/jans-lock/server-fips/pom.xml b/jans-lock/server-fips/pom.xml index 8a6a600fb6d..b816f911caf 100644 --- a/jans-lock/server-fips/pom.xml +++ b/jans-lock/server-fips/pom.xml @@ -87,10 +87,10 @@ - WEB-INF/lib/bcpkix-jdk15on-*.jar, - WEB-INF/lib/bcprov-jdk15on-*.jar, - WEB-INF/lib/bcutil-jdk15on-*.jar, - WEB-INF/lib/bcmail-jdk15on-*.jar + WEB-INF/lib/bcpkix-jdk18on-*.jar, + WEB-INF/lib/bcprov-jdk18on-*.jar, + WEB-INF/lib/bcutil-jdk18on-*.jar, + WEB-INF/lib/bcmail-jdk18on-*.jar diff --git a/jans-scim/client/pom.xml b/jans-scim/client/pom.xml index 4c00658912d..b6c7cca945c 100644 --- a/jans-scim/client/pom.xml +++ b/jans-scim/client/pom.xml @@ -77,7 +77,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on diff --git a/jans-scim/server-fips/pom.xml b/jans-scim/server-fips/pom.xml index 51b0d996af2..357baafc56f 100644 --- a/jans-scim/server-fips/pom.xml +++ b/jans-scim/server-fips/pom.xml @@ -87,10 +87,10 @@ - WEB-INF/lib/bcpkix-jdk15on-*.jar, - WEB-INF/lib/bcprov-jdk15on-*.jar, - WEB-INF/lib/bcutil-jdk15on-*.jar, - WEB-INF/lib/bcmail-jdk15on-*.jar + WEB-INF/lib/bcpkix-jdk18on-*.jar, + WEB-INF/lib/bcprov-jdk18on-*.jar, + WEB-INF/lib/bcutil-jdk18on-*.jar, + WEB-INF/lib/bcmail-jdk18on-*.jar diff --git a/jans-scim/server/pom.xml b/jans-scim/server/pom.xml index a55553f873c..225f7e2211d 100644 --- a/jans-scim/server/pom.xml +++ b/jans-scim/server/pom.xml @@ -117,11 +117,11 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on