mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8251468: X509Certificate.get{Subject,Issuer}AlternativeNames and getExtendedKeyUsage do not throw CertificateParsingException if extension is unparseable
Reviewed-by: weijun
This commit is contained in:
parent
4c3491bfa5
commit
8cc59509fe
4 changed files with 199 additions and 53 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -28,13 +28,10 @@ package sun.security.x509;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import sun.security.util.HexDumpEncoder;
|
|
||||||
|
|
||||||
import sun.security.util.*;
|
import sun.security.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,11 +106,10 @@ public class CertificateExtensions implements CertAttrSet<Extension> {
|
||||||
|
|
||||||
Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()),
|
Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()),
|
||||||
ext.getExtensionValue()};
|
ext.getExtensionValue()};
|
||||||
CertAttrSet<?> certExt = (CertAttrSet<?>)
|
CertAttrSet<?> certExt = (CertAttrSet<?>) cons.newInstance(passed);
|
||||||
cons.newInstance(passed);
|
if (map.put(certExt.getName(), (Extension)certExt) != null) {
|
||||||
if (map.put(certExt.getName(), (Extension)certExt) != null) {
|
throw new IOException("Duplicate extensions not allowed");
|
||||||
throw new IOException("Duplicate extensions not allowed");
|
}
|
||||||
}
|
|
||||||
} catch (InvocationTargetException invk) {
|
} catch (InvocationTargetException invk) {
|
||||||
Throwable e = invk.getCause();
|
Throwable e = invk.getCause();
|
||||||
if (ext.isCritical() == false) {
|
if (ext.isCritical() == false) {
|
||||||
|
@ -352,32 +348,3 @@ public class CertificateExtensions implements CertAttrSet<Extension> {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnparseableExtension extends Extension {
|
|
||||||
private String name;
|
|
||||||
private String exceptionDescription;
|
|
||||||
|
|
||||||
public UnparseableExtension(Extension ext, Throwable why) {
|
|
||||||
super(ext);
|
|
||||||
|
|
||||||
name = "";
|
|
||||||
try {
|
|
||||||
Class<?> extClass = OIDMap.getClass(ext.getExtensionId());
|
|
||||||
if (extClass != null) {
|
|
||||||
Field field = extClass.getDeclaredField("NAME");
|
|
||||||
name = (String)(field.get(null)) + " ";
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// If we cannot find the name, just ignore it
|
|
||||||
}
|
|
||||||
|
|
||||||
this.exceptionDescription = why.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public String toString() {
|
|
||||||
return super.toString() +
|
|
||||||
"Unparseable " + name + "extension due to\n" +
|
|
||||||
exceptionDescription + "\n\n" +
|
|
||||||
new HexDumpEncoder().encodeBuffer(getExtensionValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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. 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.x509;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import sun.security.util.HexDumpEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension that cannot be parsed due to decoding errors or invalid
|
||||||
|
* content.
|
||||||
|
*/
|
||||||
|
class UnparseableExtension extends Extension {
|
||||||
|
private String name;
|
||||||
|
private String exceptionDescription;
|
||||||
|
private String exceptionMessage;
|
||||||
|
|
||||||
|
UnparseableExtension(Extension ext, Throwable why) {
|
||||||
|
super(ext);
|
||||||
|
|
||||||
|
name = "";
|
||||||
|
try {
|
||||||
|
Class<?> extClass = OIDMap.getClass(ext.getExtensionId());
|
||||||
|
if (extClass != null) {
|
||||||
|
Field field = extClass.getDeclaredField("NAME");
|
||||||
|
name = (String)(field.get(null)) + " ";
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// If we cannot find the name, just ignore it
|
||||||
|
}
|
||||||
|
|
||||||
|
this.exceptionDescription = why.toString();
|
||||||
|
this.exceptionMessage = why.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
String exceptionMessage() {
|
||||||
|
return exceptionMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public String toString() {
|
||||||
|
return super.toString() +
|
||||||
|
"Unparseable " + name + "extension due to\n" +
|
||||||
|
exceptionDescription + "\n\n" +
|
||||||
|
new HexDumpEncoder().encodeBuffer(getExtensionValue());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1439,24 +1439,45 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method are the overridden implementation of
|
* This method is the overridden implementation of the
|
||||||
* getExtendedKeyUsage method in X509Certificate in the Sun
|
* getExtendedKeyUsage method in X509Certificate in the Sun
|
||||||
* provider. It is better performance-wise since it returns cached
|
* provider. It is better performance-wise since it returns cached
|
||||||
* values.
|
* values.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public synchronized List<String> getExtendedKeyUsage()
|
public synchronized List<String> getExtendedKeyUsage()
|
||||||
throws CertificateParsingException {
|
throws CertificateParsingException {
|
||||||
if (readOnly && extKeyUsage != null) {
|
if (readOnly && extKeyUsage != null) {
|
||||||
return extKeyUsage;
|
return extKeyUsage;
|
||||||
} else {
|
|
||||||
ExtendedKeyUsageExtension ext = getExtendedKeyUsageExtension();
|
|
||||||
if (ext == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
extKeyUsage =
|
|
||||||
Collections.unmodifiableList(ext.getExtendedKeyUsage());
|
|
||||||
return extKeyUsage;
|
|
||||||
}
|
}
|
||||||
|
ExtendedKeyUsageExtension ext = (ExtendedKeyUsageExtension)
|
||||||
|
getExtensionIfParseable(PKIXExtensions.ExtendedKeyUsage_Id);
|
||||||
|
if (ext == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
extKeyUsage = Collections.unmodifiableList(ext.getExtendedKeyUsage());
|
||||||
|
return extKeyUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the extension identified by OID or null if it doesn't exist
|
||||||
|
* and is not unparseable.
|
||||||
|
*
|
||||||
|
* @throws CertificateParsingException if extension is unparseable
|
||||||
|
*/
|
||||||
|
private Extension getExtensionIfParseable(ObjectIdentifier oid)
|
||||||
|
throws CertificateParsingException {
|
||||||
|
Extension ext = getExtension(oid);
|
||||||
|
if (ext == null) {
|
||||||
|
// check if unparseable
|
||||||
|
UnparseableExtension unparseableExt =
|
||||||
|
(UnparseableExtension)getUnparseableExtension(oid);
|
||||||
|
if (unparseableExt != null) {
|
||||||
|
throw new CertificateParsingException(
|
||||||
|
unparseableExt.exceptionMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1602,11 +1623,12 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method are the overridden implementation of
|
* This method is the overridden implementation of the
|
||||||
* getSubjectAlternativeNames method in X509Certificate in the Sun
|
* getSubjectAlternativeNames method in X509Certificate in the Sun
|
||||||
* provider. It is better performance-wise since it returns cached
|
* provider. It is better performance-wise since it returns cached
|
||||||
* values.
|
* values.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public synchronized Collection<List<?>> getSubjectAlternativeNames()
|
public synchronized Collection<List<?>> getSubjectAlternativeNames()
|
||||||
throws CertificateParsingException {
|
throws CertificateParsingException {
|
||||||
// return cached value if we can
|
// return cached value if we can
|
||||||
|
@ -1614,7 +1636,8 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||||
return cloneAltNames(subjectAlternativeNames);
|
return cloneAltNames(subjectAlternativeNames);
|
||||||
}
|
}
|
||||||
SubjectAlternativeNameExtension subjectAltNameExt =
|
SubjectAlternativeNameExtension subjectAltNameExt =
|
||||||
getSubjectAlternativeNameExtension();
|
(SubjectAlternativeNameExtension)getExtensionIfParseable(
|
||||||
|
PKIXExtensions.SubjectAlternativeName_Id);
|
||||||
if (subjectAltNameExt == null) {
|
if (subjectAltNameExt == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1632,7 +1655,7 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This static method is the default implementation of the
|
* This static method is the default implementation of the
|
||||||
* getSubjectAlternaitveNames method in X509Certificate. A
|
* getSubjectAlternativeNames method in X509Certificate. A
|
||||||
* X509Certificate provider generally should overwrite this to
|
* X509Certificate provider generally should overwrite this to
|
||||||
* provide among other things caching for better performance.
|
* provide among other things caching for better performance.
|
||||||
*/
|
*/
|
||||||
|
@ -1666,11 +1689,12 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method are the overridden implementation of
|
* This method is the overridden implementation of the
|
||||||
* getIssuerAlternativeNames method in X509Certificate in the Sun
|
* getIssuerAlternativeNames method in X509Certificate in the Sun
|
||||||
* provider. It is better performance-wise since it returns cached
|
* provider. It is better performance-wise since it returns cached
|
||||||
* values.
|
* values.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public synchronized Collection<List<?>> getIssuerAlternativeNames()
|
public synchronized Collection<List<?>> getIssuerAlternativeNames()
|
||||||
throws CertificateParsingException {
|
throws CertificateParsingException {
|
||||||
// return cached value if we can
|
// return cached value if we can
|
||||||
|
@ -1678,7 +1702,8 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||||
return cloneAltNames(issuerAlternativeNames);
|
return cloneAltNames(issuerAlternativeNames);
|
||||||
}
|
}
|
||||||
IssuerAlternativeNameExtension issuerAltNameExt =
|
IssuerAlternativeNameExtension issuerAltNameExt =
|
||||||
getIssuerAlternativeNameExtension();
|
(IssuerAlternativeNameExtension)getExtensionIfParseable(
|
||||||
|
PKIXExtensions.IssuerAlternativeName_Id);
|
||||||
if (issuerAltNameExt == null) {
|
if (issuerAltNameExt == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1696,7 +1721,7 @@ public class X509CertImpl extends X509Certificate implements DerEncoder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This static method is the default implementation of the
|
* This static method is the default implementation of the
|
||||||
* getIssuerAlternaitveNames method in X509Certificate. A
|
* getIssuerAlternativeNames method in X509Certificate. A
|
||||||
* X509Certificate provider generally should overwrite this to
|
* X509Certificate provider generally should overwrite this to
|
||||||
* provide among other things caching for better performance.
|
* provide among other things caching for better performance.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.security.cert.CertificateParsingException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import jdk.test.lib.security.CertUtils;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8251468
|
||||||
|
* @library /test/lib
|
||||||
|
* @summary Check that X509Certificate.getSubjectAlternativeNames,
|
||||||
|
* getIssuerAlternativeNames and getExtendedKeyUsage throw
|
||||||
|
* CertificateParsingException if extension is unparseable/invalid.
|
||||||
|
*/
|
||||||
|
public class GetUnparseableExtensions {
|
||||||
|
|
||||||
|
// Cert has 3 badly encoded extensions:
|
||||||
|
// 1. SubjectAlternativeNameExtension with a null RFC822Name
|
||||||
|
// 2. IssuerAlternativeNameExtension with a null RFC822Name
|
||||||
|
// 3. ExtendedKeyUsageExtension encoded as a Set instead of a Sequence
|
||||||
|
private final static String CERT_WITH_UNPARSEABLE_EXTS = """
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDDTCCAfWgAwIBAgIIc/2fukmBqZgwDQYJKoZIhvcNAQELBQAwDTELMAkGA1UE
|
||||||
|
AxMCQ0EwHhcNMjExMDI2MTgxNzU4WhcNMjIwMTI0MTgxNzU4WjANMQswCQYDVQQD
|
||||||
|
EwJFMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKIIIsP6ghZ94hIl
|
||||||
|
U3G+D11vASSP7JMmX6G6opGKel60dZ5So3Tdj0E6VHehwcxEdh94LEIxvOkS3Cdh
|
||||||
|
hd1iiB4/q2InvLuGzSVvWAwYNkB7GbR31pIst0GAFdH+z/tVjjHcacBGwmysk8DN
|
||||||
|
Rmxm66AyJxHA1U/v64vx5kSGNoUAEcWm+OslxfdJ7nygnU+S6a/06sRvifVn7q7c
|
||||||
|
Kg4AIFE1+vWrcgYbApkPNjLiRGAr33TAb1y+nN1zW/luEMEQIJBd+YJW9OrPGMUr
|
||||||
|
SDSXfB06HscH1bTUfN2ngsxT2hy9eiCiDtagPIwYA0sVNjyUtfPsjAH1oxDPXzC5
|
||||||
|
quJB5/sCAwEAAaNxMG8wHQYDVR0OBBYEFCqWMQBki21cSqKIHp4rFitf6pQ2MAsG
|
||||||
|
A1UdEQQEMAKBADALBgNVHRIEBDACgQAwHwYDVR0jBBgwFoAUJbvRN17hEohYsg1M
|
||||||
|
icq1mMpIwoAwEwYDVR0lBAwxCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEB
|
||||||
|
ABBqJ/yYXD1xueB63GRY4ZotO6ukEkJiPZIwrr2vZW+GMws2b9gqNoD+dL9AeYCA
|
||||||
|
Zb6RYbaNDY5OoJmEty9KbtON7Rt1LtCFuZbYKxrhW0dJgXLyNOgXr+x0g2btbvWV
|
||||||
|
r8U1icwHapZM5IqDKLivzZNzwv52mrJDuzWqmlhAIlPLIU1QfNQ1oC8HpFkL71Bb
|
||||||
|
sB/4OIxxjRzf0AGmb7aeQNfxag2oKlOwqzum1FLt8BaVjylc0aUATPtkgothK8nK
|
||||||
|
m5jXJmA4zA11ck0uJW39gDRuR0D1k4qm/s/5Iuhd6MRVDXEhVbJXuH2yzHcmbgRs
|
||||||
|
paD/he6C1JszWf7YrTsX3Fc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
""";
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface TestCase<T> {
|
||||||
|
T test() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
X509Certificate cert =
|
||||||
|
CertUtils.getCertFromString(CERT_WITH_UNPARSEABLE_EXTS);
|
||||||
|
getExtension(() -> cert.getSubjectAlternativeNames());
|
||||||
|
getExtension(() -> cert.getIssuerAlternativeNames());
|
||||||
|
getExtension(() -> cert.getExtendedKeyUsage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void getExtension(TestCase<?> t) throws Exception {
|
||||||
|
try {
|
||||||
|
t.test();
|
||||||
|
throw new Exception("Test FAILED");
|
||||||
|
} catch (CertificateParsingException cpe) {
|
||||||
|
System.out.println(cpe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue