x509credentialsauthenticationhandler.java
来自「CAS在Tomcat中实现单点登录项目,单点登录(Single Sign On 」· Java 代码 · 共 345 行
JAVA
345 行
/* * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license * distributed with this file and available online at * http://www.ja-sig.org/products/cas/overview/license/ */package org.jasig.cas.adaptors.x509.authentication.handler.support;import java.security.Principal;import java.security.cert.CertificateExpiredException;import java.security.cert.CertificateNotYetValidException;import java.security.cert.X509Certificate;import java.util.Set;import java.util.regex.Pattern;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.jasig.cas.adaptors.x509.authentication.principal.X509CertificateCredentials;import org.jasig.cas.authentication.handler.AuthenticationException;import org.jasig.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;import org.jasig.cas.authentication.principal.Credentials;import org.jasig.cas.util.annotation.NotNull;/** * Authentication Handler that accepts X509 Certificiates, determines their * validity and ensures that they were issued by a trusted issuer. (targeted at * X509v3) Optionally checks KeyUsage extension in the user certificate * (container should do that too). Note that this handler trusts the servlet * container to do some initial checks like path validation. Deployers can * supply an optional pattern to match subject dns against to further restrict * certificates in case they are not using their own issuer. It's also possible * to specify a maximum pathLength for the SUPPLIED certificates. (note that * this does not include a pathLength check for the root certificate) * [PathLength is 0 for the CA certficate that issues the end-user certificate] * * @author Scott Battaglia * @author Jan Van der Velpen * @version $Revision: 42214 $ $Date: 2007-07-05 01:20:09 -0400 (Thu, 05 Jul 2007) $ * @since 3.0.4 */public class X509CredentialsAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler { /** Default setting to limit the number of intermediate certificates. */ private static final int DEFAULT_MAXPATHLENGTH = 1; /** Default setting whether to allow unspecified number of intermediate certificates. */ private static final boolean DEFAULT_MAXPATHLENGTH_ALLOW_UNSPECIFIED = false; /** Default setting to check keyUsage extension. */ private static final boolean DEFAULT_CHECK_KEYUSAGE = false; /** Default setting to force require "KeyUsage" extension. */ private static final boolean DEFAULT_REQUIRE_KEYUSAGE = false; /** Default subject pattern match. */ private static final Pattern DEFAULT_SUBJECT_DN_PATTERN = Pattern.compile(".*"); /** Instance of Logging. */ private final Log log = LogFactory.getLog(getClass()); /** The compiled pattern supplied by the deployer. */ @NotNull private Pattern regExTrustedIssuerDnPattern; /** * Deployer supplied setting for maximum pathLength in a SUPPLIED * certificate. */ private int maxPathLength = DEFAULT_MAXPATHLENGTH; /** * Deployer supplied setting to allow unlimited pathLength in a SUPPLIED * certificate. */ private boolean maxPathLength_allowUnspecified = DEFAULT_MAXPATHLENGTH_ALLOW_UNSPECIFIED; /** Deployer supplied setting to check the KeyUsage extension. */ private boolean checkKeyUsage = DEFAULT_CHECK_KEYUSAGE; /** * Deployer supplied setting to force require the correct KeyUsage * extension. */ private boolean requireKeyUsage = DEFAULT_REQUIRE_KEYUSAGE; /** The compiled pattern for trusted DN's supplied by the deployer. */ @NotNull private Pattern regExSubjectDnPattern = DEFAULT_SUBJECT_DN_PATTERN; protected final boolean doAuthentication(final Credentials credentials) throws AuthenticationException { final X509CertificateCredentials x509Credentials = (X509CertificateCredentials) credentials; final X509Certificate[] certificates = x509Credentials .getCertificates(); /* * the certificate that was fully authenticated succesfully will be set * as the user credentials for CAS last certificate that can be set is * the end-user certificate */ X509Certificate certificateCredentialsCandidate = null; // flag to check whether a trusted issuer is in the certificate chain boolean hasTrustedIssuerInChain = false; /* * reverse transversal of certificates (should be from root to end-user * cert) */ for (int i = (certificates.length - 1); i >= 0; i--) { final X509Certificate certificate = certificates[i]; try { final Principal issuerPrincipal = certificate.getIssuerDN(); // flag that is set when this cert is an end user cert (no CA // cert) boolean isEndUserCertificate = false; if (log.isDebugEnabled()) { log.debug("--examining cert[" + certificate.getSerialNumber().toString() + "] " + certificate.getSubjectDN() + "\"" + " from issuer \"" + issuerPrincipal.getName() + "\""); } // check basic validity of the current certificate certificate.checkValidity(); log.debug("certificate is valid"); // initial check for trusted issuer in certificate chain // final check is done outside for loop if (isCertificateFromTrustedIssuer(issuerPrincipal)) { hasTrustedIssuerInChain = true; log.debug("certificate was issued by trusted issuer"); } // getBasicConstraints returns pathLenContraint which is // >=0 when this is a CA cert and -1 when it's not int pathLength = certificate.getBasicConstraints(); if (pathLength != -1) { log.debug("this is a CA certificate"); // check pathLength when CA cert //if unlimited and unlimited not allowed if (pathLength == Integer.MAX_VALUE && this.maxPathLength_allowUnspecified != true) { if (log.isWarnEnabled()) { log.warn("authentication failed; cert pathLength not specified" + " and unlimited not allowed by config [see maxPathLength_allow_unlimited]"); } return false; //else if more than allowed length } else if (pathLength > this.maxPathLength ) { if (log.isWarnEnabled()) { log.warn("authentication failed; cert pathLength [" + pathLength + "] is more than allowed by config [" + this.maxPathLength + "]"); } return false; } } else { isEndUserCertificate = true; log.debug("this is an end-user certificate"); } /* * set this certificate as the user credentials if there is an * issuer in the cert (always so if valid cert) and this is an * end-user or CA certificate (so not a CA cert) and optional * KeyUsage check */ if (issuerPrincipal != null && isEndUserCertificate && this.doesCertificateSubjectDnMatchPattern(certificate .getSubjectDN()) && (!this.checkKeyUsage || (this.checkKeyUsage && this .doesCertificateKeyUsageMatch(certificate)))) { if (log.isDebugEnabled()) { log.debug("cert[" + certificate.getSerialNumber().toString() + "] ok, setting as credentials candidate"); } certificateCredentialsCandidate = certificate; } } catch (final CertificateExpiredException e) { log.warn("authentication failed; certficiate expired [" + certificate.toString() + "]"); certificateCredentialsCandidate = null; } catch (final CertificateNotYetValidException e) { log.warn("authentication failed; certficate not yet valid [" + certificate.toString() + "]"); certificateCredentialsCandidate = null; } } // check whether one of the certificates in the chain was // from the trusted issuer; else => fail auth if (certificateCredentialsCandidate != null && hasTrustedIssuerInChain) { if (log.isInfoEnabled()) { log .info("authentication OK; SSL client authentication data meets criteria for cert[" + certificateCredentialsCandidate.getSerialNumber() .toString() + "]"); } x509Credentials.setCertificate(certificateCredentialsCandidate); return true; } if (log.isInfoEnabled()) { if (!hasTrustedIssuerInChain) { log.info("client cert did not have trusted issuer pattern \"" + this.regExTrustedIssuerDnPattern.pattern() + "\" in chain; authentication failed"); } else { log .info("authentication failed; SSL client authentication data doesn't meet criteria"); } } return false; } public void setTrustedIssuerDnPattern(final String trustedIssuerDnPattern) { this.regExTrustedIssuerDnPattern = Pattern .compile(trustedIssuerDnPattern); } /** * @param maxPathLength The maxPathLength to set. */ public void setMaxPathLength(int maxPathLength) { this.maxPathLength = maxPathLength; } /** * @param maxPathLength_allowUnspecified Allow CA certs to have unlimited intermediate certs (default=false). */ public void setMaxPathLengthAllowUnspecified(boolean maxPathLength_allowUnspecified) { this.maxPathLength_allowUnspecified = maxPathLength_allowUnspecified; } /** * @param checkKeyUsage The checkKeyUsage to set. */ public void setCheckKeyUsage(boolean checkKeyUsage) { this.checkKeyUsage = checkKeyUsage; } /** * @param requireKeyUsage The requireKeyUsage to set. */ public void setRequireKeyUsage(boolean requireKeyUsage) { this.requireKeyUsage = requireKeyUsage; } public void setSubjectDnPattern(final String subjectDnPattern) { this.regExSubjectDnPattern = Pattern.compile(subjectDnPattern); } private boolean doesCertificateKeyUsageMatch( final X509Certificate certificate) { final String extensionOID = "2.5.29.15"; final boolean keyUsage[] = certificate.getKeyUsage(); /* * KeyUsage ::= BIT STRING { digitalSignature (0), nonRepudiation (1), * keyEncipherment (2), dataEncipherment (3), keyAgreement (4), * keyCertSign (5), cRLSign (6), encipherOnly (7), decipherOnly (8) } */ if (keyUsage == null) { log.warn("isKeyUsageRequired?: " + this.requireKeyUsage + "; keyUsage not found."); return !this.requireKeyUsage; } log.debug("keyUsage extension found: examing..."); if (!isExtensionMarkedCritical(certificate, extensionOID) && !this.requireKeyUsage) { log .debug("match ok; keyUsage extension not critical and not required so not checked"); return true; } if (log.isDebugEnabled()) { log .debug("extension is marked critical in cert OR required by config" + "[critical=" + isExtensionMarkedCritical(certificate, extensionOID) + ";required=" + this.requireKeyUsage + "]"); } // we need digitalSignature for SSL client auth if (keyUsage[0]) { log.debug("match ok; keyUsage extension OK"); return true; } if (log.isWarnEnabled() && this.requireKeyUsage) { log.warn("match error; required/critical keyUsage extension fails" + "[critical=" + isExtensionMarkedCritical(certificate, extensionOID) + ";required=" + this.requireKeyUsage + "]"); } return false; } private boolean isExtensionMarkedCritical( final X509Certificate certificate, final String oid) { final Set<String> criticalOids = certificate.getCriticalExtensionOIDs(); if (criticalOids == null || criticalOids.isEmpty()) { return false; } return criticalOids.contains(oid); } private boolean doesCertificateSubjectDnMatchPattern( final Principal principal) { return doesNameMatchPattern(principal, this.regExSubjectDnPattern); } private boolean isCertificateFromTrustedIssuer(final Principal principal) { return doesNameMatchPattern(principal, this.regExTrustedIssuerDnPattern); } private boolean doesNameMatchPattern(final Principal principal, final Pattern pattern) { final boolean result = pattern.matcher(principal.getName()).matches(); if (log.isDebugEnabled()) { log.debug("Pattern Match: " + result + " [" + principal.getName() + "] against [" + pattern.pattern() + "]."); } return result; } public boolean supports(final Credentials credentials) { return credentials != null && X509CertificateCredentials.class.isAssignableFrom(credentials .getClass()); }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?