1/*
2 * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 6405536 6414980 8051972
27 * @summary Make sure that we can parse certificates using various named curves
28 *   and verify their signatures
29 * @author Andreas Sterbenz
30 * @library ..
31 * @library ../../../../java/security/testlibrary
32 * @modules jdk.crypto.cryptoki
33 * @run main/othervm ReadCertificates
34 * @run main/othervm ReadCertificates sm policy
35 */
36
37import java.io.File;
38import java.io.FileInputStream;
39import java.io.InputStream;
40import java.security.InvalidKeyException;
41import java.security.NoSuchAlgorithmException;
42import java.security.NoSuchProviderException;
43import java.security.Provider;
44import java.security.PublicKey;
45import java.security.SecureRandom;
46import java.security.SignatureException;
47import java.security.cert.CertificateException;
48import java.security.cert.CertificateFactory;
49import java.security.cert.X509Certificate;
50import java.security.interfaces.ECPublicKey;
51import java.security.spec.ECParameterSpec;
52import java.util.ArrayList;
53import java.util.Arrays;
54import java.util.Collection;
55import java.util.LinkedHashMap;
56import java.util.List;
57import java.util.Map;
58import javax.security.auth.x500.X500Principal;
59
60public class ReadCertificates extends PKCS11Test {
61
62    private static CertificateFactory factory;
63
64    private static SecureRandom random;
65
66    private static Collection<X509Certificate> readCertificates(File file) throws Exception {
67        System.out.println("Loading " + file.getName() + "...");
68        Collection<X509Certificate> certs;
69        try (InputStream in = new FileInputStream(file)) {
70            certs = (Collection<X509Certificate>)factory.generateCertificates(in);
71        }
72        return certs;
73    }
74
75    public static void main(String[] args) throws Exception {
76        main(new ReadCertificates(), args);
77    }
78
79    @Override
80    public void main(Provider p) throws Exception {
81        if (p.getService("Signature", "SHA1withECDSA") == null) {
82            System.out.println("Provider does not support ECDSA, skipping...");
83            return;
84        }
85
86        /*
87         * PKCS11Test.main will remove this provider if needed
88         */
89        Providers.setAt(p, 1);
90
91        random = new SecureRandom();
92        factory = CertificateFactory.getInstance("X.509");
93        try {
94            // clear certificate cache in from a previous run with a different
95            // provider (undocumented hack for the Sun provider)
96            factory.generateCertificate(null);
97        } catch (CertificateException e) {
98            // ignore
99        }
100        Map<X500Principal,X509Certificate> certs = new LinkedHashMap<>();
101
102        File dir = new File(BASE, "certs");
103        File closedDir = new File(CLOSED_BASE, "certs");
104        File[] files = concat(dir.listFiles(), closedDir.listFiles());
105        Arrays.sort(files);
106        for (File file : files) {
107            if (file.isFile() == false) {
108                continue;
109            }
110            Collection<X509Certificate> certList = readCertificates(file);
111            for (X509Certificate cert : certList) {
112                X509Certificate old = certs.put(cert.getSubjectX500Principal(), cert);
113                if (old != null) {
114                    System.out.println("Duplicate subject:");
115                    System.out.println("Old Certificate: " + old);
116                    System.out.println("New Certificate: " + cert);
117                    throw new Exception(file.getPath());
118                }
119            }
120        }
121        System.out.println("OK: " + certs.size() + " certificates.");
122
123        // Get supported curves
124        List<ECParameterSpec> supportedEC = getKnownCurves(p);
125
126        System.out.println("Test Certs:\n");
127        for (X509Certificate cert : certs.values()) {
128            X509Certificate issuer = certs.get(cert.getIssuerX500Principal());
129            System.out.print("Verifying " + cert.getSubjectX500Principal() +
130                    "...  ");
131            PublicKey key = issuer.getPublicKey();
132            // Check if curve is supported
133            if (issuer.getPublicKey() instanceof ECPublicKey) {
134                if (!checkSupport(supportedEC,
135                        ((ECPublicKey)key).getParams())) {
136                    System.out.println("Curve not found. Skipped.");
137                    continue;
138                }
139            }
140
141           try {
142               cert.verify(key, p.getName());
143               System.out.println("Pass.");
144           } catch (NoSuchAlgorithmException e) {
145               System.out.println("Warning: " + e.getMessage() +
146                   ". Trying another provider...");
147               cert.verify(key);
148           } catch (CertificateException | InvalidKeyException |
149                    NoSuchProviderException | SignatureException e) {
150               System.out.println(e.getMessage());
151               if (key instanceof ECPublicKey) {
152                   System.out.println("Failed.\n\tCurve: " +
153                           ((ECPublicKey)key).getParams() +
154                           "\n\tSignature Alg: " + cert.getSigAlgName());
155               } else {
156                   System.out.println("Key: "+key.toString());
157               }
158
159               System.err.println("Verifying " + cert.getSubjectX500Principal());
160               e.printStackTrace();
161           }
162        }
163
164        // try some random invalid signatures to make sure we get the correct
165        // error
166        System.out.println("Checking incorrect signatures...");
167        List<X509Certificate> certList = new ArrayList<>(certs.values());
168        for (int i = 0; i < 20; i++) {
169            X509Certificate cert, signer;
170            do {
171                cert = getRandomCert(certList);
172                signer = getRandomCert(certList);
173            } while (cert.getIssuerX500Principal().equals(signer.getSubjectX500Principal()));
174            try {
175                PublicKey signerPublicKey = signer.getPublicKey();
176                cert.verify(signerPublicKey);
177                // Ignore false positives
178                if (cert.getPublicKey().equals(signerPublicKey)) {
179                    System.out.println("OK: self-signed certificate detected");
180                } else {
181                    throw new Exception("Verified invalid signature");
182                }
183            } catch (SignatureException | InvalidKeyException e) {
184                System.out.println("OK: " + e);
185            }
186        }
187
188        System.out.println("OK");
189    }
190
191    private static X509Certificate getRandomCert(List<X509Certificate> certs) {
192        int n = random.nextInt(certs.size());
193        return certs.get(n);
194    }
195
196}
197