307 lines
11 KiB
Java
307 lines
11 KiB
Java
/*
|
|
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.security.provider.certpath;
|
|
|
|
import java.math.BigInteger;
|
|
import java.util.Collection;
|
|
import java.util.Date;
|
|
import java.util.Set;
|
|
import java.security.GeneralSecurityException;
|
|
import java.security.KeyFactory;
|
|
import java.security.PublicKey;
|
|
import java.security.SignatureException;
|
|
import java.security.cert.Certificate;
|
|
import java.security.cert.CertificateExpiredException;
|
|
import java.security.cert.CertificateNotYetValidException;
|
|
import java.security.cert.CertPathValidatorException;
|
|
import java.security.cert.CertPathValidatorException.BasicReason;
|
|
import java.security.cert.X509Certificate;
|
|
import java.security.cert.PKIXCertPathChecker;
|
|
import java.security.cert.PKIXReason;
|
|
import java.security.cert.TrustAnchor;
|
|
import java.security.interfaces.DSAParams;
|
|
import java.security.interfaces.DSAPublicKey;
|
|
import java.security.spec.DSAPublicKeySpec;
|
|
import javax.security.auth.x500.X500Principal;
|
|
import sun.security.x509.X500Name;
|
|
import sun.security.util.Debug;
|
|
|
|
/**
|
|
* BasicChecker is a PKIXCertPathChecker that checks the basic information
|
|
* on a PKIX certificate, namely the signature, timestamp, and subject/issuer
|
|
* name chaining.
|
|
*
|
|
* @since 1.4
|
|
* @author Yassir Elley
|
|
*/
|
|
class BasicChecker extends PKIXCertPathChecker {
|
|
|
|
private static final Debug debug = Debug.getInstance("certpath");
|
|
private final PublicKey trustedPubKey;
|
|
private final X500Principal caName;
|
|
private final Date date;
|
|
private final String sigProvider;
|
|
private final boolean sigOnly;
|
|
private X500Principal prevSubject;
|
|
private PublicKey prevPubKey;
|
|
|
|
/**
|
|
* Constructor that initializes the input parameters.
|
|
*
|
|
* @param anchor the anchor selected to validate the target certificate
|
|
* @param testDate the time for which the validity of the certificate
|
|
* should be determined
|
|
* @param sigProvider the name of the signature provider
|
|
* @param sigOnly true if only signature checking is to be done;
|
|
* if false, all checks are done
|
|
*/
|
|
BasicChecker(TrustAnchor anchor, Date date, String sigProvider,
|
|
boolean sigOnly) {
|
|
if (anchor.getTrustedCert() != null) {
|
|
this.trustedPubKey = anchor.getTrustedCert().getPublicKey();
|
|
this.caName = anchor.getTrustedCert().getSubjectX500Principal();
|
|
} else {
|
|
this.trustedPubKey = anchor.getCAPublicKey();
|
|
this.caName = anchor.getCA();
|
|
}
|
|
this.date = date;
|
|
this.sigProvider = sigProvider;
|
|
this.sigOnly = sigOnly;
|
|
this.prevPubKey = trustedPubKey;
|
|
}
|
|
|
|
/**
|
|
* Initializes the internal state of the checker from parameters
|
|
* specified in the constructor.
|
|
*/
|
|
@Override
|
|
public void init(boolean forward) throws CertPathValidatorException {
|
|
if (!forward) {
|
|
prevPubKey = trustedPubKey;
|
|
if (PKIX.isDSAPublicKeyWithoutParams(prevPubKey)) {
|
|
// If TrustAnchor is a DSA public key and it has no params, it
|
|
// cannot be used to verify the signature of the first cert,
|
|
// so throw exception
|
|
throw new CertPathValidatorException("Key parameters missing");
|
|
}
|
|
prevSubject = caName;
|
|
} else {
|
|
throw new
|
|
CertPathValidatorException("forward checking not supported");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isForwardCheckingSupported() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Set<String> getSupportedExtensions() {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Performs the signature, timestamp, and subject/issuer name chaining
|
|
* checks on the certificate using its internal state. This method does
|
|
* not remove any critical extensions from the Collection.
|
|
*
|
|
* @param cert the Certificate
|
|
* @param unresolvedCritExts a Collection of the unresolved critical
|
|
* extensions
|
|
* @throws CertPathValidatorException if certificate does not verify
|
|
*/
|
|
@Override
|
|
public void check(Certificate cert, Collection<String> unresolvedCritExts)
|
|
throws CertPathValidatorException
|
|
{
|
|
X509Certificate currCert = (X509Certificate)cert;
|
|
|
|
if (!sigOnly) {
|
|
verifyTimestamp(currCert);
|
|
verifyNameChaining(currCert);
|
|
}
|
|
verifySignature(currCert);
|
|
|
|
updateState(currCert);
|
|
}
|
|
|
|
/**
|
|
* Verifies the signature on the certificate using the previous public key.
|
|
*
|
|
* @param cert the X509Certificate
|
|
* @throws CertPathValidatorException if certificate does not verify
|
|
*/
|
|
private void verifySignature(X509Certificate cert)
|
|
throws CertPathValidatorException
|
|
{
|
|
String msg = "signature";
|
|
if (debug != null)
|
|
debug.println("---checking " + msg + "...");
|
|
|
|
try {
|
|
if (sigProvider != null) {
|
|
cert.verify(prevPubKey, sigProvider);
|
|
} else {
|
|
cert.verify(prevPubKey);
|
|
}
|
|
} catch (SignatureException e) {
|
|
throw new CertPathValidatorException
|
|
(msg + " check failed", e, null, -1,
|
|
BasicReason.INVALID_SIGNATURE);
|
|
} catch (GeneralSecurityException e) {
|
|
throw new CertPathValidatorException(msg + " check failed", e);
|
|
}
|
|
|
|
if (debug != null)
|
|
debug.println(msg + " verified.");
|
|
}
|
|
|
|
/**
|
|
* Internal method to verify the timestamp on a certificate
|
|
*/
|
|
private void verifyTimestamp(X509Certificate cert)
|
|
throws CertPathValidatorException
|
|
{
|
|
String msg = "timestamp";
|
|
if (debug != null)
|
|
debug.println("---checking " + msg + ":" + date.toString() + "...");
|
|
|
|
try {
|
|
cert.checkValidity(date);
|
|
} catch (CertificateExpiredException e) {
|
|
throw new CertPathValidatorException
|
|
(msg + " check failed", e, null, -1, BasicReason.EXPIRED);
|
|
} catch (CertificateNotYetValidException e) {
|
|
throw new CertPathValidatorException
|
|
(msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID);
|
|
}
|
|
|
|
if (debug != null)
|
|
debug.println(msg + " verified.");
|
|
}
|
|
|
|
/**
|
|
* Internal method to check that cert has a valid DN to be next in a chain
|
|
*/
|
|
private void verifyNameChaining(X509Certificate cert)
|
|
throws CertPathValidatorException
|
|
{
|
|
if (prevSubject != null) {
|
|
|
|
String msg = "subject/issuer name chaining";
|
|
if (debug != null)
|
|
debug.println("---checking " + msg + "...");
|
|
|
|
X500Principal currIssuer = cert.getIssuerX500Principal();
|
|
|
|
// reject null or empty issuer DNs
|
|
if (X500Name.asX500Name(currIssuer).isEmpty()) {
|
|
throw new CertPathValidatorException
|
|
(msg + " check failed: " +
|
|
"empty/null issuer DN in certificate is invalid", null,
|
|
null, -1, PKIXReason.NAME_CHAINING);
|
|
}
|
|
|
|
if (!(currIssuer.equals(prevSubject))) {
|
|
throw new CertPathValidatorException
|
|
(msg + " check failed", null, null, -1,
|
|
PKIXReason.NAME_CHAINING);
|
|
}
|
|
|
|
if (debug != null)
|
|
debug.println(msg + " verified.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internal method to manage state information at each iteration
|
|
*/
|
|
private void updateState(X509Certificate currCert)
|
|
throws CertPathValidatorException
|
|
{
|
|
PublicKey cKey = currCert.getPublicKey();
|
|
if (debug != null) {
|
|
debug.println("BasicChecker.updateState issuer: " +
|
|
currCert.getIssuerX500Principal().toString() + "; subject: " +
|
|
currCert.getSubjectX500Principal() + "; serial#: " +
|
|
currCert.getSerialNumber().toString());
|
|
}
|
|
if (PKIX.isDSAPublicKeyWithoutParams(cKey)) {
|
|
// cKey needs to inherit DSA parameters from prev key
|
|
cKey = makeInheritedParamsKey(cKey, prevPubKey);
|
|
if (debug != null) debug.println("BasicChecker.updateState Made " +
|
|
"key with inherited params");
|
|
}
|
|
prevPubKey = cKey;
|
|
prevSubject = currCert.getSubjectX500Principal();
|
|
}
|
|
|
|
/**
|
|
* Internal method to create a new key with inherited key parameters.
|
|
*
|
|
* @param keyValueKey key from which to obtain key value
|
|
* @param keyParamsKey key from which to obtain key parameters
|
|
* @return new public key having value and parameters
|
|
* @throws CertPathValidatorException if keys are not appropriate types
|
|
* for this operation
|
|
*/
|
|
static PublicKey makeInheritedParamsKey(PublicKey keyValueKey,
|
|
PublicKey keyParamsKey) throws CertPathValidatorException
|
|
{
|
|
if (!(keyValueKey instanceof DSAPublicKey) ||
|
|
!(keyParamsKey instanceof DSAPublicKey))
|
|
throw new CertPathValidatorException("Input key is not " +
|
|
"appropriate type for " +
|
|
"inheriting parameters");
|
|
DSAParams params = ((DSAPublicKey)keyParamsKey).getParams();
|
|
if (params == null)
|
|
throw new CertPathValidatorException("Key parameters missing");
|
|
try {
|
|
BigInteger y = ((DSAPublicKey)keyValueKey).getY();
|
|
KeyFactory kf = KeyFactory.getInstance("DSA");
|
|
DSAPublicKeySpec ks = new DSAPublicKeySpec(y,
|
|
params.getP(),
|
|
params.getQ(),
|
|
params.getG());
|
|
return kf.generatePublic(ks);
|
|
} catch (GeneralSecurityException e) {
|
|
throw new CertPathValidatorException("Unable to generate key with" +
|
|
" inherited parameters: " +
|
|
e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return the public key associated with the last certificate processed
|
|
*
|
|
* @return PublicKey the last public key processed
|
|
*/
|
|
PublicKey getPublicKey() {
|
|
return prevPubKey;
|
|
}
|
|
}
|