1 /*
2  * Copyright (c) 2015, 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 8074064
27  * @summary OCSPResponse.SingleResponse objects do not parse singleExtensions
28  * @modules java.base/sun.security.x509
29  *          java.base/sun.security.provider.certpath
30  * @run main/othervm OCSPSingleExtensions
31  */
32 
33 import java.io.*;
34 import java.util.*;
35 import java.security.cert.*;
36 
37 import sun.security.x509.SerialNumber;
38 import sun.security.provider.certpath.*;
39 
40 /*
41  * Tester note:
42  * For this test, all input files should be co-located with the test source
43  * code.  All test input data should be in PEM format, and may be commented
44  * with the '#' character at the beginning of any comment line.  Most tests were
45  * generated using the "openssl ocsp" utility in server mode and used the same
46  * utility as a client to drive the responses.  In rare cases
47  * (ocsp-good-witharchcut.resp, etc.) the test input was manually modified
48  * because openssl's ocsp could not generate data in that format (e.g. a
49  * "good" response with singleExtensions in the SingleResponse structure.)
50  * These tests were created to force the code to walk codepaths reached only
51  * with invalid OCSP data or legal formats that are not easily generated using
52  * the tools at hand.  These hand-modified test cases will not verify.
53  */
54 
55 public class OCSPSingleExtensions {
56     public static CertificateFactory CF;
57     public static final File testDir =
58             new File(System.getProperty("test.src", "."));
59     public static final Base64.Decoder B64D = Base64.getMimeDecoder();
60 
main(String [] args)61     public static void main(String [] args) throws Exception {
62         // Get a CertificateFactory for various tests
63         CF = CertificateFactory.getInstance("X509");
64         ByteArrayInputStream bais =
65                 new ByteArrayInputStream(readFile("int.crt").getBytes());
66         X509Certificate intCA = (X509Certificate)CF.generateCertificate(bais);
67         System.out.println("Successfully instantiated CA cert \"" +
68                 intCA.getSubjectX500Principal() + "\"");
69 
70         CertId cid0x1500 = new CertId(intCA, new SerialNumber(0x1500));
71         boolean noFailures = true;
72 
73         OCSPResponse.SingleResponse sr =
74                 getSRByFilename("ocsp-good-nonext.resp", cid0x1500);
75         noFailures &= checkSingleExts(sr, 0);
76 
77         if (sr.getRevocationTime() != null) {
78             throw new RuntimeException("Oops. revocationTime is non-null " +
79                     sr.getRevocationTime());
80         } else if (sr.getRevocationReason() != null) {
81             throw new RuntimeException("Oops. revocationReason is non-null " +
82                     sr.getRevocationReason());
83         }
84 
85         sr = getSRByFilename("ocsp-good-withnext.resp", cid0x1500);
86         noFailures &= checkSingleExts(sr, 0);
87 
88         sr = getSRByFilename("ocsp-good-witharchcut.resp", cid0x1500);
89         noFailures &= checkSingleExts(sr, 1);
90 
91         sr = getSRByFilename("ocsp-rev-nocerts.resp", cid0x1500);
92         noFailures &= checkSingleExts(sr, 1);
93 
94         sr = getSRByFilename("ocsp-rev-nonext-noinv.resp", cid0x1500);
95         noFailures &= checkSingleExts(sr, 0);
96 
97         sr = getSRByFilename("ocsp-rev-withnext-noinv.resp", cid0x1500);
98         noFailures &= checkSingleExts(sr, 0);
99 
100         sr = getSRByFilename("ocsp-rev-nonext-withinv.resp", cid0x1500);
101         noFailures &= checkSingleExts(sr, 1);
102 
103         sr = getSRByFilename("ocsp-rev-withnext-withinv.resp", cid0x1500);
104         noFailures &= checkSingleExts(sr, 1);
105 
106         try {
107             sr = getSRByFilename("ocsp-rev-twonext.resp", cid0x1500);
108             System.out.println("FAIL: Allowed two nextUpdate fields");
109             noFailures = false;
110         } catch (IOException ioe) {
111             System.out.println("Caught expected exception: " + ioe);
112         }
113 
114         try {
115             sr = getSRByFilename("ocsp-rev-bad-sr-tag.resp", cid0x1500);
116             System.out.println("FAIL: Allowed invalid singleResponse item");
117             noFailures = false;
118         } catch (IOException ioe) {
119             System.out.println("Caught expected exception: " + ioe);
120         }
121 
122         try {
123             sr = getSRByFilename("ocsp-rev-sr-cont-reverse.resp", cid0x1500);
124             System.out.println("FAIL: Allowed reversed " +
125                     "nextUpdate/singleExtensions");
126             noFailures = false;
127         } catch (IOException ioe) {
128             System.out.println("Caught expected exception: " + ioe);
129         }
130 
131         if (!noFailures) {
132             throw new RuntimeException("One or more tests failed");
133         }
134     }
135 
getSRByFilename(String fileName, CertId cid)136     private static OCSPResponse.SingleResponse getSRByFilename(String fileName,
137             CertId cid) throws IOException {
138         byte[] respDER = B64D.decode(readFile(fileName));
139         OCSPResponse or = new OCSPResponse(respDER);
140         OCSPResponse.SingleResponse sr = or.getSingleResponse(cid);
141         return sr;
142     }
143 
readFile(String fileName)144     private static String readFile(String fileName) throws IOException {
145         String filePath = testDir + "/" + fileName;
146         StringBuilder sb = new StringBuilder();
147 
148         try (FileReader fr = new FileReader(filePath);
149                 BufferedReader br = new BufferedReader(fr)) {
150             String line;
151             while ((line = br.readLine()) != null) {
152                 if (!line.trim().startsWith("#")) {
153                     sb.append(line).append("\n");
154                 }
155             }
156         }
157 
158         System.out.println("Successfully read " + fileName);
159         return sb.toString();
160     }
161 
checkSingleExts(OCSPResponse.SingleResponse sr, int singleExtCount)162     private static boolean checkSingleExts(OCSPResponse.SingleResponse sr,
163             int singleExtCount) {
164         Map<String, Extension> singleExts;
165         try {
166             singleExts = sr.getSingleExtensions();
167         } catch (NullPointerException npe) {
168             System.out.println(
169                     "Warning: Sent null singleResponse into checkSingleExts");
170             return false;
171         }
172 
173         for (String key : singleExts.keySet()) {
174             System.out.println("singleExtension: " + singleExts.get(key));
175         }
176 
177         if (singleExts.size() != singleExtCount) {
178             System.out.println("Single Extension count mismatch, " +
179                     "expected " + singleExtCount + ", got " +
180                     singleExts.size());
181             return false;
182         } else {
183             return true;
184         }
185     }
186 }
187