-
Bug
-
Resolution: Fixed
-
P2
-
1.4.0
-
rc1
-
x86
-
windows_2000
Name: nt126004 Date: 10/25/2001
java version "1.4.0-beta2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta2-b77)
Java HotSpot(TM) Client VM (build 1.4.0-beta2-b77, mixed mode)
When attempting to build a certificate path via the CertPathBuilder class
(using "PKIX"), if the target certificate does not have the Authority Key
Identifier extension, a call to the CertPathBuilder build() method results in a
NullPointerException with no other explanation. It should probably throw a more
appropriate exception and give a better error message.
The documentation seems to be OK [for the build(CertPathParameters) method
of the java.security.cert.CertPathBuilder class]. In particular it says that a
CertPathBuilderException is thrown "if the builder is unable to construct a certification
path that satisfies the specified parameters."
In the case where the target certificate does not have the Authority Key Identifier
extension, the builder is unable to construct a path, but instead of throwing a
CertPathBuilderException, a NullPointerException is thrown. So the behavior
disagrees with the documentation. (Note also that the documentation does not list
any precondition that the certificate must have the Authority Key Identifier extension).
Since I don't have access to the JDK1.4 source code, it's hard to tell why a
NullPointerException is being thrown, but my best guess is that the developer of that
code may have unconsciously assumed that any target certificate would have the Authority
Key Identifier extension, and it is simply throwing a NullPointerException when it is not there.
In summary, I believe the NullPointerException is not the correct response because it does
not agree with the documentation.
However, with respect to the documentation, it might be beneficial to point out in the
documentation that target certificates need to have the Authority Key Identifier extension (although I suppose
that it is assumed that anyone working the API needs to be familiar with the PKIX specification).
Source code to reproduce the problem:
package misc;
import java.security.cert.X509Certificate;
import java.security.cert.TrustAnchor;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.CertStore;
import java.security.cert.X509CertSelector;
import java.security.cert.CertPathBuilder;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.CertPathBuilderResult;
import java.security.cert.CertificateFactory;
import java.security.cert.CRL;
import java.security.cert.CertPath;
import java.util.HashSet;
import java.util.ArrayList;
import java.io.ByteArrayInputStream;
import sun.misc.BASE64Decoder;
/**
* This is a simple test class that attempts to make use of the certificate
* path building API (part of the JDK1.4 Certificate Path API).
*
* Potential bug or undesirable feature in JDK:
* If the certificate does not have the Authority Key Identifier extension, an
* attempt to build the chain results in a NullPointerException with no other
* explanation. It should probably throw a more appropriate exception and give
* a better error message.
*
* Instructions:
* Simply run the class. The first certificate should work. The second one
* will fail.
*
* Configuration:
* -Windows 2000 Professional
* -java version "1.4.0-beta2"
* -Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta2-
b77)
* -Java HotSpot(TM) Client VM (build 1.4.0-beta2-b77, mixed mode)
*/
public class Certs {
public static void main(String[] args) {
try {
Certs certs = new Certs();
// the first certificate has the Authority Key Identifier
extension
certs.doBuild(getUserCertificate1());
System.out.println("successfully built path for the first
certificate");
System.out.println();
System.out.println();
// the second certificate does not have the Authority Key
Identifier extension
// this will not succeed
certs.doBuild(getUserCertificate2());
System.out.println("successfully built path for the second
certificate");
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void doBuild(X509Certificate userCert) throws Exception {
// get the set of trusted CA certificates (only one in this instance)
HashSet trustAnchors = new HashSet();
X509Certificate trustedCert = getTrustedCertificate();
trustAnchors.add(new TrustAnchor(trustedCert, null));
// put together a CertStore (repository of the certificates and CRLs)
ArrayList certs = new ArrayList();
CRL crl = getCRL();
certs.add(crl);
certs.add(trustedCert);
certs.add(userCert);
CollectionCertStoreParameters certStoreParams = new
CollectionCertStoreParameters(certs);
CertStore certStore = CertStore.getInstance("Collection",
certStoreParams);
// specify the target certificate via a CertSelector
X509CertSelector certSelector = new X509CertSelector();
certSelector.setCertificate(userCert);
certSelector.setSubject(userCert.getSubjectDN().getName()); // seems to
be required
// build a valid cerificate path
CertPathBuilder certPathBuilder = CertPathBuilder.getInstance
("PKIX");
PKIXBuilderParameters certPathBuilderParams = new PKIXBuilderParameters
(trustAnchors, certSelector);
certPathBuilderParams.addCertStore(certStore);
CertPathBuilderResult result = certPathBuilder.build
(certPathBuilderParams);
// get and show cert path
CertPath certPath = result.getCertPath();
System.out.println(certPath.toString());
}
private static X509Certificate getTrustedCertificate() throws Exception
{
// this certificate has the Authority Key Identifier extension.
// it was issued by some other self-signed certificate, but we
// simply treat this certificate as the trusted one
String sCert =
"-----BEGIN CERTIFICATE-----\n"
+ "MIIBezCCASWgAwIBAgIQyWD8dLUoqpJFyDxrfRlrsTANBgkqhkiG9w0BAQQFADAW\n"
+ "MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMTEwMTkxMjU5MjZaFw0zOTEyMzEy\n"
+ "MzU5NTlaMBoxGDAWBgNVBAMTD1Jvb3RDZXJ0aWZpY2F0ZTBcMA0GCSqGSIb3DQEB\n"
+ "AQUAA0sAMEgCQQC+NFKszPjatUZKWmyWaFjir1wB93FX2u5SL+GMjgUsMs1JcTKQ\n"
+ "Kh0cnnQKknNkV4cTW4NPn31YCoB1+0KA3mknAgMBAAGjSzBJMEcGA1UdAQRAMD6A\n"
+ "EBLkCS0GHR1PAI1hIdwWZGOhGDAWMRQwEgYDVQQDEwtSb290IEFnZW5jeYIQBjds\n"
+ "AKoAZIoRz7jUqlw19DANBgkqhkiG9w0BAQQFAANBACJxAfP57yqaT9N+nRgAOugM\n"
+ "JG0aN3/peCIvL3p29epRL2xoWFvxpUUlsH2I39OZ6b8+twWCebhkv1I62segXAk=\n"
+ "-----END CERTIFICATE-----";
CertificateFactory certFactory = CertificateFactory.getInstance
("X.509");
ByteArrayInputStream bytes = new ByteArrayInputStream(sCert.getBytes());
return (X509Certificate)certFactory.generateCertificate(bytes);
}
private static X509Certificate getUserCertificate1() throws Exception
{
// this certificate has the Authority Key Identifier extension.
String sCert =
"-----BEGIN CERTIFICATE-----\n"
+ "MIIBfzCCASmgAwIBAgIQWFSKzCWO2ptOAc2F3MKZSzANBgkqhkiG9w0BAQQFADAa\n"
+ "MRgwFgYDVQQDEw9Sb290Q2VydGlmaWNhdGUwHhcNMDExMDE5MTMwNzQxWhcNMzkx\n"
+ "MjMxMjM1OTU5WjAaMRgwFgYDVQQDEw9Vc2VyQ2VydGlmaWNhdGUwXDANBgkqhkiG\n"
+ "9w0BAQEFAANLADBIAkEA24gypa2YFGZHKznEWWbqIWNVXCM35W7RwJwhGpNsuBCj\n"
+ "NT6KEo66F+OOMgZmb0KrEZHBJASJ3n4Cqbt4aHm/2wIDAQABo0swSTBHBgNVHQEE\n"
+ "QDA+gBBch+eYzOPgVRbMq5vGpVWooRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mC\n"
+ "EMlg/HS1KKqSRcg8a30Za7EwDQYJKoZIhvcNAQEEBQADQQCYBIHBqQQJePi5Hzfo\n"
+ "CxeUaYlXmvbxVNkxM65Pplsj3h4ntfZaynmlhahH3YsnnA8wk6xPt04LjSId12RB\n"
+ "PeuO\n"
+ "-----END CERTIFICATE-----";
CertificateFactory certFactory = CertificateFactory.getInstance
("X.509");
ByteArrayInputStream bytes = new ByteArrayInputStream(sCert.getBytes());
return (X509Certificate)certFactory.generateCertificate(bytes);
}
private static X509Certificate getUserCertificate2() throws Exception {
// this certificate DOES NOT have the Authority Key Identifier
extension.
String sCert =
"-----BEGIN CERTIFICATE-----\n"
+ "MIIBMjCB3aADAgECAhB6225ckZVssEukPuvk1U1PMA0GCSqGSIb3DQEBBAUAMBox\n"
+ "GDAWBgNVBAMTD1Jvb3RDZXJ0aWZpY2F0ZTAeFw0wMTEwMTkxNjA5NTZaFw0wMjEw\n"
+ "MTkyMjA5NTZaMBsxGTAXBgNVBAMTEFVzZXJDZXJ0aWZpY2F0ZTIwXDANBgkqhkiG\n"
+ "9w0BAQEFAANLADBIAkEAzicGiW9aUlUoQIZnLy1l8MMV5OvA+4VJ4T/xo/PpN8Oq\n"
+ "WgZVGKeEp6JCzMlXEJk3TGLfpXL4Ytw+Ldhv0QPhLwIDAnMpMA0GCSqGSIb3DQEB\n"
+ "BAUAA0EAQmj9SFHEx66JyAps3ew4pcSS3QvfVZ/6qsNUYCG75rFGcTUPHcXKql9y\n"
+ "qBT83iNLJ//krjw5Ju0WRPg/buHSww==\n"
+ "-----END CERTIFICATE-----";
CertificateFactory certFactory = CertificateFactory.getInstance
("X.509");
ByteArrayInputStream bytes = new ByteArrayInputStream(sCert.getBytes());
return (X509Certificate)certFactory.generateCertificate(bytes);
}
private static CRL getCRL() throws Exception {
// this crl is empty (nothing on the list)
String crlBase64 =
"MIGdMEkwDQYJKoZIhvcNAQEFBQAwGjEYMBYGA1UEAwwPUm9vdENlcnRpZmljYXRlFw0
wMTEwMTkx\n"
+ "MzE0MDNaFw0wMTExMDkxNjUwMzBaMA0GCSqGSIb3DQEBBQUAA0EAcVnmwqjhfF9NCcX5Ct14KJamn"
+ "TBiDkxpNCCtJh3902toleJiY6AakPAW+EsqES8q7JvB80GNK0nEpJDWEkVh9ww==";
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] crlBytes = base64Decoder.decodeBuffer(crlBase64);
ByteArrayInputStream bytes = new ByteArrayInputStream
(crlBytes);
CertificateFactory certFactory = CertificateFactory.getInstance
("X.509");
CRL crl = certFactory.generateCRL(bytes);
return crl;
}
}
Program output:
X.509 Cert Path: length = 1.
[
=========================================================Certificate 1 start.
[
[
Version: V3
Subject: CN=UserCertificate
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4
Key: com.sun.net.ssl.internal.ssl.JSA_RSAPublicKey@aeffdf
Validity: [From: Fri Oct 19 09:07:41 EDT 2001,
To: Sat Dec 31 18:59:59 EST 2039]
Issuer: CN=RootCertificate
SerialNumber: [ 58548acc 258eda9b 4e01cd85 dcc2994b ]
Certificate Extensions: 1
[1]: ObjectId: 2.5.29.1 Criticality=false
Extension unknown: DER encoded OCTET string =
0000: 04 40 30 3E 80 10 5C 87 E7 98 CC E3 E0 55 16 CC .@0>..\......U..
0010: AB 9B C6 A5 55 A8 A1 18 30 16 31 14 30 12 06 03 ....U...0.1.0...
0020: 55 04 03 13 0B 52 6F 6F 74 20 41 67 65 6E 63 79 U....Root Agency
0030: 82 10 C9 60 FC 74 B5 28 AA 92 45 C8 3C 6B 7D 19 ...`.t.(..E.<k..
0040: 6B B1 k.
]
Algorithm: [MD5withRSA]
Signature:
0000: 98 04 81 C1 A9 04 09 78 F8 B9 1F 37 E8 0B 17 94 .......x...7....
0010: 69 89 57 9A F6 F1 54 D9 31 33 AE 4F A6 5B 23 DE i.W...T.13.O.[#.
0020: 1E 27 B5 F6 5A CA 79 A5 85 A8 47 DD 8B 27 9C 0F .'..Z.y...G..'..
0030: 30 93 AC 4F B7 4E 0B 8D 22 1D D7 64 41 3D EB 8E 0..O.N.."..dA=..
]
=========================================================Certificate 1 end.
]
successfully built path for the first certificate
sun.security.provider.certpath.SunCertPathBuilderException: unable to find
valid certification path to requested target; internal cause is:
java.lang.NullPointerException
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild
(SunCertPathBuilder.java:196)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:343)
at misc.Certs.doBuild(Certs.java:84)
at misc.Certs.main(Certs.java:53)
Caused by: java.lang.NullPointerException
at
sun.security.provider.certpath.SunCertPathBuilder.depthFirstSearchForward
(SunCertPathBuilder.java:514)
at
sun.security.provider.certpath.SunCertPathBuilder.depthFirstSearchForward
(SunCertPathBuilder.java:577)
at sun.security.provider.certpath.SunCertPathBuilder.buildForward
(SunCertPathBuilder.java:315)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild
(SunCertPathBuilder.java:187)
... 3 more
(Review ID: 134101)
======================================================================