--- /dev/null 2016-03-30 11:33:15.169715293 +0300 +++ new/test/java/security/cert/X509CertSelectorTest.java 2016-04-06 18:36:40.747210410 +0300 @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2000, 2016, 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 static sun.security.x509.GeneralNameInterface.NAME_DIRECTORY; +import static sun.security.x509.NameConstraintsExtension.EXCLUDED_SUBTREES; +import static sun.security.x509.NameConstraintsExtension.PERMITTED_SUBTREES; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.CertificatePoliciesExtension; +import sun.security.x509.DNSName; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNameInterface; +import sun.security.x509.GeneralNames; +import sun.security.x509.GeneralSubtree; +import sun.security.x509.GeneralSubtrees; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.NameConstraintsExtension; +import sun.security.x509.PolicyInformation; +import sun.security.x509.PrivateKeyUsageExtension; +import sun.security.x509.SubjectAlternativeNameExtension; +import sun.security.x509.X500Name; + +/* + * @test + * @bug 8074931 + * @summary This class tests the X509CertSelector. The tests check particular criteria + * by setting them to a value that should match our test certificate and + * ensuring that they do match, then setting them to a value that should not + * match our test certificate and ensuring that they do not match. + */ +public class X509CertSelectorTest { + /* + Certificate: + Data: + Version: 3 (0x2) + Serial Number: 954172088 (0x38df82b8) + Signature Algorithm: dsaWithSHA1 + Issuer: C=us, O=sun, OU=testing + Validity + Not Before: Mar 27 15:48:08 2000 GMT + Not After : Jun 25 14:48:08 2000 GMT + Subject: C=us, O=sun, OU=testing, CN=mullan + Subject Public Key Info: + Public Key Algorithm: dsaEncryption + pub: 0 + P: 0 + Q: 0 + G: 0 + X509v3 extensions: + X509v3 Name Constraints: critical + 0D.B0@.>1.0...U....us1.0 + ..U. + ..sun1.0...U....testing1.0 + ..U....mullan + X509v3 Subject Key Identifier: + 56:E8:88:AE:9D:B5:3F:2B:CB:A0:4C:4B:E2:87:53:07:33:77:1B:DF + X509v3 Authority Key Identifier: + keyid:8E:DD:AF:6F:EE:02:12:F4:61:E9:2F:E3:64:1A:6F:71:32:25:20:C0 + + X509v3 Subject Alternative Name: + email:mullan@east.sun.com + X509v3 Private Key Usage Period: + Not Before: Jan 1 05:00:00 2000 GMT, Not After: Jan 1 05:00:00 2001 GMT + X509v3 Key Usage: critical + Digital Signature + X509v3 Certificate Policies: + 0$0\..*...0.0...+.......0.. + Testing... + Signature Algorithm: dsaWithSHA1 + r: + 44:c7:35:40:5d:6c:28:75:7f:73:b2:f8:0d:72:6c: + 09:65:b8:81:14 + s: + 76:79:f5:c7:37:3b:0d:9b:db:70:2f:20:80:36:e3: + 80:e8:a6:c6:71 + */ + private static final String testCert = + "-----BEGIN CERTIFICATE-----\n" + + "MIICLjCCAeygAwIBAgIEON+CuDALBgcqhkjOOAQDBQAwLTELMAkGA1UEBhMCdXMx\n" + + "DDAKBgNVBAoTA3N1bjEQMA4GA1UECxMHdGVzdGluZzAeFw0wMDAzMjcxNTQ4MDha\n" + + "Fw0wMDA2MjUxNDQ4MDhaMD4xCzAJBgNVBAYTAnVzMQwwCgYDVQQKEwNzdW4xEDAO\n" + + "BgNVBAsTB3Rlc3RpbmcxDzANBgNVBAMTBm11bGxhbjAcMBQGByqGSM44BAEwCQIB\n" + + "AAIBAAIBAAMEAAIBAKOCASMwggEfMFAGA1UdHgEB/wRGMESgQjBApD4xCzAJBgNV\n" + + "BAYTAnVzMQwwCgYDVQQKEwNzdW4xEDAOBgNVBAsTB3Rlc3RpbmcxDzANBgNVBAMT\n" + + "Bm11bGxhbjAdBgNVHQ4EFgQUVuiIrp21PyvLoExL4odTBzN3G98wHwYDVR0jBBgw\n" + + "FoAUjt2vb+4CEvRh6S/jZBpvcTIlIMAwHgYDVR0RBBcwFYETbXVsbGFuQGVhc3Qu\n" + + "c3VuLmNvbTArBgNVHRAEJDAigA8yMDAwMDEwMTA1MDAwMFqBDzIwMDEwMTAxMDUw\n" + + "MDAwWjAPBgNVHQ8BAf8EBQMDB4AAMC0GA1UdIAQmMCQwIgYEKoSAADAaMBgGCCsG\n" + + "AQUFBwICMAwSClRlc3RpbmcuLi4wCwYHKoZIzjgEAwUAAy8AMCwCFETHNUBdbCh1\n" + + "f3Oy+A1ybAlluIEUAhR2efXHNzsNm9twLyCANuOA6KbGcQ==\n" + + "-----END CERTIFICATE-----\n" + + ""; + + private static final String testKey = + "MIIBtjCCASsGByqGSM44BAEwggEeAoGBAIVWPEkcxbxhQRCqVzg55tNqbP5j0K4kdu4bkmXvfqC5\n" + + "+qA75DvnfzsOJseb+9AuKXWk/DvCzFDmrY1YaU3scZC3OQEO9lEO3F4VDKOaudY6OT1SI22pAIwz\n" + + "j5pvq+i7zOp4xUqkQUeh/4iQSfxOT5UrFGjkcbnbpVkCXD/GxAz7AhUAjtnm3dVIddUUHl6wxpZ7\n" + + "GcA6gSsCgYAf/PXzQtemgIDjpFrNNSgTEKkLposBXKatAM+gUKlMUjf8SQvquqPxDtRrscGjXkoL\n" + + "oTkaR7/akULYFpBvUcFkeIFiCnJg8M9XhCWdLvn9MPt+jR2oxookvCb9xLtD6WvIM/wd/nZ1iK4u\n" + + "iY1+q85xvns/Awbtwl7oZDAwE2TUKAOBhAACgYBDc9UZ+3xsZubUZvRG5cpyJceYpJp2exOPVJXn\n" + + "jR4CcR+cT9bAJpFSxqE/8KtNHXxHdu4f3DU67IMOVDpugzihyzXJvNm3w2H9x+6xczHG2wjvAJeh\n" + + "X62EWbUatxPXFAoVKZWuUbaYaZzdWBDtNRrCuKKsLo0GFy8g2BZISuD3jw==\n" + + ""; + + // Certificate to run tests on + private final X509Certificate cert; + + public static void main(String[] args) throws Exception { + X509CertSelectorTest test = new X509CertSelectorTest(); + test.doTest(); + } + + public X509CertSelectorTest() throws CertificateException, IOException { + cert = (X509Certificate) CertificateFactory.getInstance("X.509") + .generateCertificate(new ByteArrayInputStream(testCert.getBytes())); + } + + // Runs the test. + private void doTest() throws Exception { + System.out.println("START OF TESTS FOR " + "X509CertSelector"); + + testSerialNumber(); + testIssuer(); + testSubjectKeyIdentifier(); + testAuthorityKeyIdentifier(); + testCertificateValid(); + testPrivateKeyValid(); + testSubjectPublicKeyAlgID(); + testKeyUsage(); + testSubjectAltName(); + testPolicy(); + testPathToName(); + testSubject(); + testSubjectPublicKey(); + testNameConstraints(); + testBasicConstraints(); + testCertificate(); + } + + // Tests matching on the serial number contained in the certificate. + private void testSerialNumber() { + System.out.println("X.509 Certificate Match on serialNumber"); + // bad match + X509CertSelector selector = new X509CertSelector(); + selector.setSerialNumber(new BigInteger("999999999")); + checkMatch(selector, cert, false); + + // good match + selector.setSerialNumber(cert.getSerialNumber()); + checkMatch(selector, cert, true); + } + + // Tests matching on the issuer name contained in the certificate. + private void testIssuer() throws IOException { + System.out.println("X.509 Certificate Match on issuer"); + // bad match + X509CertSelector selector = new X509CertSelector(); + selector.setIssuer("ou=bogus,ou=east,o=sun,c=us"); + checkMatch(selector, cert, false); + + // good match + selector.setIssuer((cert.getIssuerX500Principal()).getName("RFC2253")); + checkMatch(selector, cert, true); + } + + /* + * Tests matching on the subject key identifier contained in the + * certificate. + */ + private void testSubjectKeyIdentifier() throws IOException { + System.out.println("X.509 Certificate Match on subjectKeyIdentifier"); + // bad match + X509CertSelector selector = new X509CertSelector(); + byte[] b = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + selector.setSubjectKeyIdentifier(b); + checkMatch(selector, cert, false); + + // good match + DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.14")); + byte[] encoded = in.getOctetString(); + selector.setSubjectKeyIdentifier(encoded); + checkMatch(selector, cert, true); + } + + /* + * Tests matching on the authority key identifier contained in the + * certificate. + */ + private void testAuthorityKeyIdentifier() throws IOException { + System.out.println("X.509 Certificate Match on authorityKeyIdentifier"); + // bad match + X509CertSelector selector = new X509CertSelector(); + byte[] b = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + AuthorityKeyIdentifierExtension a = new AuthorityKeyIdentifierExtension(new KeyIdentifier(b), null, null); + selector.setAuthorityKeyIdentifier(a.getExtensionValue()); + checkMatch(selector, cert, false); + + // good match + DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.35")); + byte[] encoded = in.getOctetString(); + selector.setAuthorityKeyIdentifier(encoded); + checkMatch(selector, cert, true); + } + + /* + * Tests matching on the certificate validity component contained in the + * certificate. + */ + private void testCertificateValid() { + System.out.println("X.509 Certificate Match on certificateValid"); + // bad match + X509CertSelector selector = new X509CertSelector(); + Calendar cal = Calendar.getInstance(); + cal.set(1968, 12, 31); + selector.setCertificateValid(cal.getTime()); + checkMatch(selector, cert, false); + + // good match + selector.setCertificateValid(cert.getNotBefore()); + checkMatch(selector, cert, true); + } + + /* + * Tests matching on the private key validity component contained in the + * certificate. + */ + private void testPrivateKeyValid() throws IOException, CertificateException { + System.out.println("X.509 Certificate Match on privateKeyValid"); + // bad match + X509CertSelector selector = new X509CertSelector(); + Calendar cal = Calendar.getInstance(); + cal.set(1968, 12, 31); + selector.setPrivateKeyValid(cal.getTime()); + checkMatch(selector, cert, false); + + // good match + DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.16")); + byte[] encoded = in.getOctetString(); + PrivateKeyUsageExtension ext = new PrivateKeyUsageExtension(false, encoded); + Date validDate = (Date) ext.get(PrivateKeyUsageExtension.NOT_BEFORE); + selector.setPrivateKeyValid(validDate); + checkMatch(selector, cert, true); + + } + + private ObjectIdentifier getCertPubKeyAlgOID(X509Certificate xcert) throws IOException { + byte[] encodedKey = xcert.getPublicKey().getEncoded(); + DerValue val = new DerValue(encodedKey); + if (val.tag != DerValue.tag_Sequence) { + throw new RuntimeException("invalid key format"); + } + + return AlgorithmId.parse(val.data.getDerValue()).getOID(); + } + + /* + * Tests matching on the subject public key algorithm ID component contained + * in the certificate. + */ + private void testSubjectPublicKeyAlgID() throws IOException { + System.out.println("X.509 Certificate Match on subjectPublicKeyAlgID"); + // bad match + X509CertSelector selector = new X509CertSelector(); + selector.setSubjectPublicKeyAlgID("2.5.29.14"); + checkMatch(selector, cert, false); + + // good match + selector.setSubjectPublicKeyAlgID(getCertPubKeyAlgOID(cert).toString()); + checkMatch(selector, cert, true); + + } + + // Tests matching on the key usage extension contained in the certificate. + private void testKeyUsage() { + System.out.println("X.509 Certificate Match on keyUsage"); + // bad match + X509CertSelector selector = new X509CertSelector(); + boolean[] keyUsage = { true, false, true, false, true, false, true, false }; + selector.setKeyUsage(keyUsage); + System.out.println("Selector = " + selector.toString()); + checkMatch(selector, cert, false); + + // good match + selector.setKeyUsage(cert.getKeyUsage()); + System.out.println("Selector = " + selector.toString()); + checkMatch(selector, cert, true); + } + + /* + * Tests matching on the subject alternative name extension contained in the + * certificate. + */ + private void testSubjectAltName() throws IOException { + System.out.println("X.509 Certificate Match on subjectAltName"); + // bad match + X509CertSelector selector = new X509CertSelector(); + GeneralNameInterface dnsName = new DNSName("foo.com"); + DerOutputStream tmp = new DerOutputStream(); + dnsName.encode(tmp); + selector.addSubjectAlternativeName(2, tmp.toByteArray()); + checkMatch(selector, cert, false); + + // good match + DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.17")); + byte[] encoded = in.getOctetString(); + SubjectAlternativeNameExtension ext = new SubjectAlternativeNameExtension(false, encoded); + GeneralNames names = (GeneralNames) ext.get(SubjectAlternativeNameExtension.SUBJECT_NAME); + GeneralName name = (GeneralName) names.get(0); + selector.setSubjectAlternativeNames(null); + DerOutputStream tmp2 = new DerOutputStream(); + name.getName().encode(tmp2); + selector.addSubjectAlternativeName(name.getType(), tmp2.toByteArray()); + checkMatch(selector, cert, true); + + // good match 2 (matches at least one) + selector.setMatchAllSubjectAltNames(false); + selector.addSubjectAlternativeName(2, "foo.com"); + checkMatch(selector, cert, true); + } + + /* + * Tests matching on the policy constraints extension contained in the + * certificate. + */ + private void testPolicy() throws IOException { + System.out.println("X.509 Certificate Match on certificatePolicies"); + // test encoding of CertificatePoliciesExtension because we wrote the + // code + // bad match + X509CertSelector selector = new X509CertSelector(); + Set s = new HashSet<>(); + s.add(new String("1.2.5.7.68")); + selector.setPolicy(s); + checkMatch(selector, cert, false); + + // good match + DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.32")); + CertificatePoliciesExtension ext = new CertificatePoliciesExtension(false, in.getOctetString()); + List policies = ext.get(CertificatePoliciesExtension.POLICIES); + // match on the first policy id + PolicyInformation policyInfo = (PolicyInformation) policies.get(0); + s.clear(); + s.add(policyInfo.getPolicyIdentifier().getIdentifier().toString()); + selector.setPolicy(s); + checkMatch(selector, cert, true); + } + + /* + * Tests matching on the name constraints extension contained in the + * certificate. + */ + private void testPathToName() throws IOException { + System.out.println("X.509 Certificate Match on pathToName"); + + X509CertSelector selector = null; + DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.30")); + byte[] encoded = in.getOctetString(); + NameConstraintsExtension ext = new NameConstraintsExtension(false, encoded); + GeneralSubtrees permitted = (GeneralSubtrees) ext.get(PERMITTED_SUBTREES); + GeneralSubtrees excluded = (GeneralSubtrees) ext.get(EXCLUDED_SUBTREES); + + // bad matches on pathToName within excluded subtrees + if (excluded != null) { + Iterator e = excluded.iterator(); + while (e.hasNext()) { + GeneralSubtree tree = e.next(); + if (tree.getName().getType() == NAME_DIRECTORY) { + X500Name excludedDN1 = new X500Name(tree.getName().toString()); + X500Name excludedDN2 = new X500Name("CN=Bogus, " + tree.getName().toString()); + DerOutputStream derDN1 = new DerOutputStream(); + DerOutputStream derDN2 = new DerOutputStream(); + excludedDN1.encode(derDN1); + excludedDN2.encode(derDN2); + selector = new X509CertSelector(); + selector.addPathToName(NAME_DIRECTORY, derDN1.toByteArray()); + checkMatch(selector, cert, false); + selector.setPathToNames(null); + selector.addPathToName(NAME_DIRECTORY, derDN2.toByteArray()); + checkMatch(selector, cert, false); + } + } + } + + // good matches on pathToName within permitted subtrees + if (permitted != null) { + Iterator e = permitted.iterator(); + while (e.hasNext()) { + GeneralSubtree tree = e.next(); + if (tree.getName().getType() == NAME_DIRECTORY) { + X500Name permittedDN1 = new X500Name(tree.getName().toString()); + X500Name permittedDN2 = new X500Name("CN=good, " + tree.getName().toString()); + DerOutputStream derDN1 = new DerOutputStream(); + DerOutputStream derDN2 = new DerOutputStream(); + permittedDN1.encode(derDN1); + permittedDN2.encode(derDN2); + selector = new X509CertSelector(); + selector.addPathToName(NAME_DIRECTORY, derDN1.toByteArray()); + checkMatch(selector, cert, true); + selector.setPathToNames(null); + selector.addPathToName(NAME_DIRECTORY, derDN2.toByteArray()); + checkMatch(selector, cert, true); + } + } + } + } + + // Tests matching on the subject name contained in the certificate. + private void testSubject() throws IOException { + System.out.println("X.509 Certificate Match on subject"); + // bad match + X509CertSelector selector = new X509CertSelector(); + selector.setSubject("ou=bogus,ou=east,o=sun,c=us"); + checkMatch(selector, cert, false); + + // good match + selector.setSubject(cert.getSubjectX500Principal().getName("RFC2253")); + checkMatch(selector, cert, true); + } + + // Tests matching on the subject public key contained in the certificate. + private void testSubjectPublicKey() throws IOException, GeneralSecurityException { + System.out.println("X.509 Certificate Match on subject public key"); + // bad match + X509CertSelector selector = new X509CertSelector(); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec( + Base64.getMimeDecoder().decode(testKey.getBytes())); + KeyFactory keyFactory = KeyFactory.getInstance("DSA"); + PublicKey pubKey = keyFactory.generatePublic(keySpec); + selector.setSubjectPublicKey(pubKey); + checkMatch(selector, cert, false); + + // good match + selector.setSubjectPublicKey(cert.getPublicKey()); + checkMatch(selector, cert, true); + } + + // Tests matching on the name constraints contained in the certificate. + private void testNameConstraints() throws IOException { + System.out.println("X.509 Certificate Match on name constraints"); + // bad match + GeneralSubtrees subjectTree = new GeneralSubtrees(); + subjectTree.add(getGeneralSubtree((X500Name) cert.getSubjectDN())); + NameConstraintsExtension ext = new NameConstraintsExtension((GeneralSubtrees) null, subjectTree); + X509CertSelector selector = new X509CertSelector(); + selector.setNameConstraints(ext.getExtensionValue()); + checkMatch(selector, cert, false); + + // good match + ext = new NameConstraintsExtension(subjectTree, null); + selector.setNameConstraints(ext.getExtensionValue()); + checkMatch(selector, cert, true); + } + + // Tests matching on basic constraints. + private void testBasicConstraints() { + System.out.println("X.509 Certificate Match on basic constraints"); + // bad match + X509CertSelector selector = new X509CertSelector(); + int mpl = cert.getBasicConstraints(); + selector.setBasicConstraints(0); + checkMatch(selector, cert, false); + + // good match + selector.setBasicConstraints(mpl); + checkMatch(selector, cert, true); + } + + // Tests certificateEquals criterion + private void testCertificate() { + System.out.println("X.509 Certificate Match on certificateEquals criterion"); + + X509CertSelector selector = new X509CertSelector(); + // good match + selector.setCertificate(cert); + checkMatch(selector, cert, true); + } + + private void checkMatch(X509CertSelector selector, X509Certificate cert, boolean match) { + boolean result = selector.match(cert); + if (match != result) + throw new RuntimeException(selector + " match " + cert + " is " + result + ", but expect " + match); + } + + private static GeneralSubtree getGeneralSubtree(GeneralNameInterface gni) { + // Create a new GeneralSubtree with the specified name, 0 base, and + // unlimited length + GeneralName gn = new GeneralName(gni); + GeneralSubtree subTree = new GeneralSubtree(gn, 0, -1); + return subTree; + } +}