8270946: X509CertImpl.getFingerprint should not return the empty String
Reviewed-by: weijun
This commit is contained in:
parent
45d277feb0
commit
fc80a6b493
@ -70,8 +70,14 @@ public class AnchorCertificates {
|
||||
if (alias.contains(" [jdk")) {
|
||||
X509Certificate cert = (X509Certificate) cacerts
|
||||
.getCertificate(alias);
|
||||
certs.add(X509CertImpl.getFingerprint(HASH, cert));
|
||||
certIssuers.add(cert.getSubjectX500Principal());
|
||||
String fp =
|
||||
X509CertImpl.getFingerprint(HASH, cert, debug);
|
||||
// only add trust anchor if fingerprint can
|
||||
// be calculated
|
||||
if (fp != null) {
|
||||
certs.add(fp);
|
||||
certIssuers.add(cert.getSubjectX500Principal());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,8 +99,8 @@ public class AnchorCertificates {
|
||||
* @return true if the certificate is a JDK trust anchor
|
||||
*/
|
||||
public static boolean contains(X509Certificate cert) {
|
||||
String key = X509CertImpl.getFingerprint(HASH, cert);
|
||||
boolean result = certs.contains(key);
|
||||
String key = X509CertImpl.getFingerprint(HASH, cert, debug);
|
||||
boolean result = (key == null ? false : certs.contains(key));
|
||||
if (result && debug != null) {
|
||||
debug.println("AnchorCertificate.contains: matched " +
|
||||
cert.getSubjectX500Principal());
|
||||
|
@ -28,7 +28,6 @@ import java.io.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Properties;
|
||||
|
||||
import jdk.internal.util.StaticProperty;
|
||||
@ -80,17 +79,9 @@ public final class UntrustedCertificates {
|
||||
if (algorithm == null) {
|
||||
return false;
|
||||
}
|
||||
String key;
|
||||
if (cert instanceof X509CertImpl) {
|
||||
key = ((X509CertImpl)cert).getFingerprint(algorithm);
|
||||
} else {
|
||||
try {
|
||||
key = new X509CertImpl(cert.getEncoded()).getFingerprint(algorithm);
|
||||
} catch (CertificateException cee) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return props.containsKey(key);
|
||||
// if fingerprint cannot be calculated, also treat it as untrusted
|
||||
String key = X509CertImpl.getFingerprint(algorithm, cert, debug);
|
||||
return (key == null || props.containsKey(key));
|
||||
}
|
||||
|
||||
private UntrustedCertificates() {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2021, 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
|
||||
@ -32,6 +32,7 @@ import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.security.util.Debug;
|
||||
import sun.security.x509.X509CertImpl;
|
||||
|
||||
/**
|
||||
@ -40,6 +41,8 @@ import sun.security.x509.X509CertImpl;
|
||||
*/
|
||||
final class SymantecTLSPolicy {
|
||||
|
||||
private static final Debug debug = Debug.getInstance("certpath");
|
||||
|
||||
// SHA-256 certificate fingerprints of distrusted roots
|
||||
private static final Set<String> FINGERPRINTS = Set.of(
|
||||
// cacerts alias: geotrustglobalca
|
||||
@ -154,14 +157,24 @@ final class SymantecTLSPolicy {
|
||||
static void checkDistrust(X509Certificate[] chain)
|
||||
throws ValidatorException {
|
||||
X509Certificate anchor = chain[chain.length-1];
|
||||
if (FINGERPRINTS.contains(fingerprint(anchor))) {
|
||||
String fp = fingerprint(anchor);
|
||||
if (fp == null) {
|
||||
throw new ValidatorException("Cannot generate fingerprint for "
|
||||
+ "trust anchor of TLS server certificate");
|
||||
}
|
||||
if (FINGERPRINTS.contains(fp)) {
|
||||
Date notBefore = chain[0].getNotBefore();
|
||||
LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(),
|
||||
ZoneOffset.UTC);
|
||||
// check if chain goes through one of the subCAs
|
||||
if (chain.length > 2) {
|
||||
X509Certificate subCA = chain[chain.length-2];
|
||||
LocalDate distrustDate = EXEMPT_SUBCAS.get(fingerprint(subCA));
|
||||
fp = fingerprint(subCA);
|
||||
if (fp == null) {
|
||||
throw new ValidatorException("Cannot generate fingerprint "
|
||||
+ "for intermediate CA of TLS server certificate");
|
||||
}
|
||||
LocalDate distrustDate = EXEMPT_SUBCAS.get(fp);
|
||||
if (distrustDate != null) {
|
||||
// reject if certificate is issued after specified date
|
||||
checkNotBefore(ldNotBefore, distrustDate, anchor);
|
||||
@ -174,9 +187,7 @@ final class SymantecTLSPolicy {
|
||||
}
|
||||
|
||||
private static String fingerprint(X509Certificate cert) {
|
||||
return (cert instanceof X509CertImpl)
|
||||
? ((X509CertImpl)cert).getFingerprint("SHA-256")
|
||||
: X509CertImpl.getFingerprint("SHA-256", cert);
|
||||
return X509CertImpl.getFingerprint("SHA-256", cert, debug);
|
||||
}
|
||||
|
||||
private static void checkNotBefore(LocalDate notBeforeDate,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2021, 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
|
||||
@ -1917,25 +1917,57 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||
private ConcurrentHashMap<String,String> fingerprints =
|
||||
new ConcurrentHashMap<>(2);
|
||||
|
||||
public String getFingerprint(String algorithm) {
|
||||
private String getFingerprint(String algorithm, Debug debug) {
|
||||
return fingerprints.computeIfAbsent(algorithm,
|
||||
x -> getFingerprint(x, this));
|
||||
x -> {
|
||||
try {
|
||||
return getFingerprintInternal(x, getEncodedInternal(), debug);
|
||||
} catch (CertificateEncodingException e) {
|
||||
if (debug != null) {
|
||||
debug.println("Cannot encode certificate: " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String getFingerprintInternal(String algorithm,
|
||||
byte[] encodedCert, Debug debug) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(algorithm);
|
||||
byte[] digest = md.digest(encodedCert);
|
||||
return HexFormat.of().withUpperCase().formatHex(digest);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
if (debug != null) {
|
||||
debug.println("Cannot create " + algorithm
|
||||
+ " MessageDigest: " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the requested finger print of the certificate. The result
|
||||
* Gets the requested fingerprint of the certificate. The result
|
||||
* only contains 0-9 and A-F. No small case, no colon.
|
||||
*
|
||||
* @param algorithm the MessageDigest algorithm
|
||||
* @param cert the X509Certificate
|
||||
* @return the fingerprint, or null if it cannot be calculated because
|
||||
* of an exception
|
||||
*/
|
||||
public static String getFingerprint(String algorithm,
|
||||
X509Certificate cert) {
|
||||
try {
|
||||
byte[] encCertInfo = cert.getEncoded();
|
||||
MessageDigest md = MessageDigest.getInstance(algorithm);
|
||||
byte[] digest = md.digest(encCertInfo);
|
||||
return HexFormat.of().withUpperCase().formatHex(digest);
|
||||
} catch (NoSuchAlgorithmException | CertificateEncodingException e) {
|
||||
// ignored
|
||||
X509Certificate cert, Debug debug) {
|
||||
if (cert instanceof X509CertImpl) {
|
||||
return ((X509CertImpl)cert).getFingerprint(algorithm, debug);
|
||||
} else {
|
||||
try {
|
||||
return getFingerprintInternal(algorithm, cert.getEncoded(), debug);
|
||||
} catch (CertificateEncodingException e) {
|
||||
if (debug != null) {
|
||||
debug.println("Cannot encode certificate: " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8270946
|
||||
* @library /test/lib
|
||||
* @modules java.base/sun.security.x509
|
||||
* java.base/sun.security.util
|
||||
* @summary Check that X509CertImpl.getFingerprint does not return null when
|
||||
* there are errors calculating the fingerprint
|
||||
*/
|
||||
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import sun.security.x509.X509CertImpl;
|
||||
import sun.security.util.Debug;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.security.CertUtils;
|
||||
|
||||
public class GetFingerprintError {
|
||||
|
||||
private static final Debug dbg = Debug.getInstance("certpath");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
X509Certificate cert = CertUtils.getCertFromString(CertUtils.RSA_CERT);
|
||||
|
||||
// test invalid MessageDigest algorithm
|
||||
Asserts.assertNull(X509CertImpl.getFingerprint("NoSuchAlg", cert, dbg));
|
||||
|
||||
// test cert with bad encoding
|
||||
X509Certificate fcert = new X509CertificateWithBadEncoding(cert);
|
||||
Asserts.assertNull(X509CertImpl.getFingerprint("SHA-256", fcert, dbg));
|
||||
}
|
||||
|
||||
private static class X509CertificateWithBadEncoding
|
||||
extends CertUtils.ForwardingX509Certificate {
|
||||
private X509CertificateWithBadEncoding(X509Certificate cert) {
|
||||
super(cert);
|
||||
}
|
||||
@Override
|
||||
public byte[] getEncoded() throws CertificateEncodingException {
|
||||
throw new CertificateEncodingException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
@ -35,19 +35,28 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CRLException;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.cert.CertPathBuilder;
|
||||
import java.security.cert.CertPathValidator;
|
||||
import java.security.cert.CertStore;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.CollectionCertStoreParameters;
|
||||
import java.security.cert.PKIXBuilderParameters;
|
||||
import java.security.cert.PKIXCertPathBuilderResult;
|
||||
@ -59,6 +68,7 @@ import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -630,4 +640,67 @@ public class CertUtils {
|
||||
throw new RuntimeException("Cannot read file", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is useful for overriding one or more methods of an
|
||||
* X509Certificate for testing purposes.
|
||||
*/
|
||||
public static class ForwardingX509Certificate extends X509Certificate {
|
||||
private final X509Certificate cert;
|
||||
public ForwardingX509Certificate(X509Certificate cert) {
|
||||
this.cert = cert;
|
||||
}
|
||||
public Set<String> getCriticalExtensionOIDs() {
|
||||
return cert.getCriticalExtensionOIDs();
|
||||
}
|
||||
public byte[] getExtensionValue(String oid) {
|
||||
return cert.getExtensionValue(oid);
|
||||
}
|
||||
public Set<String> getNonCriticalExtensionOIDs() {
|
||||
return cert.getNonCriticalExtensionOIDs();
|
||||
}
|
||||
public boolean hasUnsupportedCriticalExtension() {
|
||||
return cert.hasUnsupportedCriticalExtension();
|
||||
}
|
||||
public void checkValidity() throws CertificateExpiredException,
|
||||
CertificateNotYetValidException { /* always pass */ }
|
||||
public void checkValidity(Date date) throws CertificateExpiredException,
|
||||
CertificateNotYetValidException { /* always pass */ }
|
||||
public int getVersion() { return cert.getVersion(); }
|
||||
public BigInteger getSerialNumber() { return cert.getSerialNumber(); }
|
||||
public Principal getIssuerDN() { return cert.getIssuerDN(); }
|
||||
public Principal getSubjectDN() { return cert.getSubjectDN(); }
|
||||
public Date getNotBefore() { return cert.getNotBefore(); }
|
||||
public Date getNotAfter() { return cert.getNotAfter(); }
|
||||
public byte[] getTBSCertificate() throws CertificateEncodingException {
|
||||
return cert.getTBSCertificate();
|
||||
}
|
||||
public byte[] getSignature() { return cert.getSignature(); }
|
||||
public String getSigAlgName() { return cert.getSigAlgName(); }
|
||||
public String getSigAlgOID() { return cert.getSigAlgOID(); }
|
||||
public byte[] getSigAlgParams() { return cert.getSigAlgParams(); }
|
||||
public boolean[] getIssuerUniqueID() {
|
||||
return cert.getIssuerUniqueID();
|
||||
}
|
||||
public boolean[] getSubjectUniqueID() {
|
||||
return cert.getSubjectUniqueID();
|
||||
}
|
||||
public boolean[] getKeyUsage() { return cert.getKeyUsage(); }
|
||||
public int getBasicConstraints() { return cert.getBasicConstraints(); }
|
||||
public byte[] getEncoded() throws CertificateEncodingException {
|
||||
return cert.getEncoded();
|
||||
}
|
||||
public void verify(PublicKey key) throws CertificateException,
|
||||
InvalidKeyException, NoSuchAlgorithmException,
|
||||
NoSuchProviderException, SignatureException {
|
||||
cert.verify(key);
|
||||
}
|
||||
public void verify(PublicKey key, String sigProvider) throws
|
||||
CertificateException, InvalidKeyException, NoSuchAlgorithmException,
|
||||
NoSuchProviderException, SignatureException {
|
||||
cert.verify(key, sigProvider);
|
||||
}
|
||||
public PublicKey getPublicKey() { return cert.getPublicKey(); }
|
||||
public String toString() { return cert.toString(); }
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user