1 /*
2  * Copyright (c) 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 8171319 8177569 8182879
27  * @summary keytool should print out warnings when reading or generating
28   *         cert/cert req using weak algorithms
29  * @library /test/lib
30  * @modules java.base/sun.security.tools.keytool
31  *          java.base/sun.security.tools
32  *          java.base/sun.security.util
33  * @build jdk.test.lib.SecurityTools
34  *        jdk.test.lib.Utils
35  *        jdk.test.lib.Asserts
36  *        jdk.test.lib.JDKToolFinder
37  *        jdk.test.lib.JDKToolLauncher
38  *        jdk.test.lib.Platform
39  *        jdk.test.lib.process.*
40  * @run main/othervm/timeout=600 -Duser.language=en -Duser.country=US WeakAlg
41  */
42 
43 import jdk.test.lib.Asserts;
44 import jdk.test.lib.SecurityTools;
45 import jdk.test.lib.process.OutputAnalyzer;
46 import sun.security.tools.KeyStoreUtil;
47 import sun.security.util.DisabledAlgorithmConstraints;
48 
49 import java.io.ByteArrayInputStream;
50 import java.io.ByteArrayOutputStream;
51 import java.io.File;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.PrintStream;
55 import java.nio.file.Files;
56 import java.nio.file.Paths;
57 import java.nio.file.StandardCopyOption;
58 import java.security.CryptoPrimitive;
59 import java.security.KeyStore;
60 import java.security.cert.X509Certificate;
61 import java.util.Collections;
62 import java.util.EnumSet;
63 import java.util.Set;
64 import java.util.stream.Collectors;
65 import java.util.stream.Stream;
66 
67 public class WeakAlg {
68 
main(String[] args)69     public static void main(String[] args) throws Throwable {
70 
71         rm("ks");
72 
73         // -genkeypair, and -printcert, -list -alias, -exportcert
74         // (w/ different formats)
75         checkGenKeyPair("a", "-keyalg RSA -sigalg MD5withRSA", "MD5withRSA");
76         checkGenKeyPair("b", "-keyalg RSA -keysize 512", "512-bit RSA key");
77         checkGenKeyPair("c", "-keyalg RSA", null);
78 
79         kt("-list")
80                 .shouldContain("Warning:")
81                 .shouldMatch("<a>.*MD5withRSA.*risk")
82                 .shouldMatch("<b>.*512-bit RSA key.*risk");
83         kt("-list -v")
84                 .shouldContain("Warning:")
85                 .shouldMatch("<a>.*MD5withRSA.*risk")
86                 .shouldContain("MD5withRSA (weak)")
87                 .shouldMatch("<b>.*512-bit RSA key.*risk")
88                 .shouldContain("512-bit RSA key (weak)");
89 
90         // Multiple warnings for multiple cert in -printcert
91         // or -list or -exportcert
92 
93         // -certreq, -printcertreq, -gencert
94         checkCertReq("a", "", null);
95         gencert("c-a", "")
96                 .shouldNotContain("Warning"); // new sigalg is not weak
97         gencert("c-a", "-sigalg MD2withRSA")
98                 .shouldContain("Warning:")
99                 .shouldMatch("The generated certificate.*MD2withRSA.*risk");
100 
101         checkCertReq("a", "-sigalg MD5withRSA", "MD5withRSA");
102         gencert("c-a", "")
103                 .shouldContain("Warning:")
104                 .shouldMatch("The certificate request.*MD5withRSA.*risk");
105         gencert("c-a", "-sigalg MD2withRSA")
106                 .shouldContain("Warning:")
107                 .shouldMatch("The certificate request.*MD5withRSA.*risk")
108                 .shouldMatch("The generated certificate.*MD2withRSA.*risk");
109 
110         checkCertReq("b", "", "512-bit RSA key");
111         gencert("c-b", "")
112                 .shouldContain("Warning:")
113                 .shouldMatch("The certificate request.*512-bit RSA key.*risk")
114                 .shouldMatch("The generated certificate.*512-bit RSA key.*risk");
115 
116         checkCertReq("c", "", null);
117         gencert("a-c", "")
118                 .shouldContain("Warning:")
119                 .shouldMatch("The issuer.*MD5withRSA.*risk");
120 
121         // but the new cert is not weak
122         kt("-printcert -file a-c.cert")
123                 .shouldNotContain("Warning")
124                 .shouldNotContain("weak");
125 
126         gencert("b-c", "")
127                 .shouldContain("Warning:")
128                 .shouldMatch("The issuer.*512-bit RSA key.*risk");
129 
130         // -importcert
131         checkImport();
132 
133         // -importkeystore
134         checkImportKeyStore();
135 
136         // -gencrl, -printcrl
137 
138         checkGenCRL("a", "", null);
139         checkGenCRL("a", "-sigalg MD5withRSA", "MD5withRSA");
140         checkGenCRL("b", "", "512-bit RSA key");
141         checkGenCRL("c", "", null);
142 
143         kt("-delete -alias b");
144         kt("-printcrl -file b.crl")
145                 .shouldContain("WARNING: not verified");
146 
147         jksTypeCheck();
148 
149         checkInplaceImportKeyStore();
150     }
151 
jksTypeCheck()152     static void jksTypeCheck() throws Exception {
153 
154         // No warning for cacerts, all certs
155         kt0("-cacerts -list -storepass changeit")
156                 .shouldNotContain("Warning:");
157 
158         rm("ks");
159         rm("ks2");
160 
161         kt("-genkeypair -keyalg DSA -alias a -dname CN=A")
162                 .shouldNotContain("Warning:");
163         kt("-list")
164                 .shouldNotContain("Warning:");
165         kt("-list -storetype jks") // no warning if PKCS12 used as JKS
166                 .shouldNotContain("Warning:");
167         kt("-exportcert -alias a -file a.crt")
168                 .shouldNotContain("Warning:");
169 
170         // warn if migrating to JKS
171         importkeystore("ks", "ks2", "-deststoretype jks")
172                 .shouldContain("JKS keystore uses a proprietary format");
173 
174         rm("ks");
175         rm("ks2");
176         rm("ks3");
177 
178         // no warning if all certs
179         kt("-importcert -alias b -file a.crt -storetype jks -noprompt")
180                 .shouldNotContain("Warning:");
181         kt("-genkeypair -alias a -dname CN=A")
182                 .shouldContain("JKS keystore uses a proprietary format");
183         kt("-list")
184                 .shouldContain("JKS keystore uses a proprietary format");
185         kt("-list -storetype pkcs12") // warn if JKS used as PKCS12
186                 .shouldContain("JKS keystore uses a proprietary format");
187         kt("-exportcert -alias a -file a.crt")
188                 .shouldContain("JKS keystore uses a proprietary format");
189         kt("-printcert -file a.crt") // no warning if keystore not touched
190                 .shouldNotContain("Warning:");
191         kt("-certreq -alias a -file a.req")
192                 .shouldContain("JKS keystore uses a proprietary format");
193         kt("-printcertreq -file a.req") // no warning if keystore not touched
194                 .shouldNotContain("Warning:");
195 
196         // No warning if migrating from JKS
197         importkeystore("ks", "ks2", "")
198                 .shouldNotContain("Warning:");
199 
200         importkeystore("ks", "ks3", "-deststoretype pkcs12")
201                 .shouldNotContain("Warning:");
202 
203         rm("ks");
204 
205         kt("-genkeypair -alias a -dname CN=A -storetype jceks")
206                 .shouldContain("JCEKS keystore uses a proprietary format");
207         kt("-list")
208                 .shouldContain("JCEKS keystore uses a proprietary format");
209         kt("-importcert -alias b -file a.crt -noprompt")
210                 .shouldContain("JCEKS keystore uses a proprietary format");
211         kt("-exportcert -alias a -file a.crt")
212                 .shouldContain("JCEKS keystore uses a proprietary format");
213         kt("-printcert -file a.crt")
214                 .shouldNotContain("Warning:");
215         kt("-certreq -alias a -file a.req")
216                 .shouldContain("JCEKS keystore uses a proprietary format");
217         kt("-printcertreq -file a.req")
218                 .shouldNotContain("Warning:");
219         kt("-genseckey -alias c -keyalg AES -keysize 128")
220                 .shouldContain("JCEKS keystore uses a proprietary format");
221     }
222 
checkImportKeyStore()223     static void checkImportKeyStore() throws Exception {
224 
225         rm("ks2");
226         rm("ks3");
227 
228         importkeystore("ks", "ks2", "")
229                 .shouldContain("3 entries successfully imported")
230                 .shouldContain("Warning")
231                 .shouldMatch("<b>.*512-bit RSA key.*risk")
232                 .shouldMatch("<a>.*MD5withRSA.*risk");
233 
234         importkeystore("ks", "ks3", "-srcalias a")
235                 .shouldContain("Warning")
236                 .shouldMatch("<a>.*MD5withRSA.*risk");
237     }
238 
checkInplaceImportKeyStore()239     static void checkInplaceImportKeyStore() throws Exception {
240 
241         rm("ks");
242         genkeypair("a", "");
243 
244         // Same type backup
245         importkeystore("ks", "ks", "")
246                 .shouldContain("Warning:")
247                 .shouldMatch("original.*ks.old");
248 
249         importkeystore("ks", "ks", "")
250                 .shouldContain("Warning:")
251                 .shouldMatch("original.*ks.old2");
252 
253         importkeystore("ks", "ks", "-srcstoretype jks") // it knows real type
254                 .shouldContain("Warning:")
255                 .shouldMatch("original.*ks.old3");
256 
257         String cPath = new File("ks").getCanonicalPath();
258 
259         importkeystore("ks", cPath, "")
260                 .shouldContain("Warning:")
261                 .shouldMatch("original.*ks.old4");
262 
263         // Migration
264         importkeystore("ks", "ks", "-deststoretype jks")
265                 .shouldContain("Warning:")
266                 .shouldContain("JKS keystore uses a proprietary format")
267                 .shouldMatch("Migrated.*JKS.*PKCS12.*ks.old5");
268 
269         Asserts.assertEQ(
270                 KeyStore.getInstance(
271                         new File("ks"), "changeit".toCharArray()).getType(),
272                 "JKS");
273 
274         importkeystore("ks", "ks", "-srcstoretype PKCS12")
275                 .shouldContain("Warning:")
276                 .shouldNotContain("proprietary format")
277                 .shouldMatch("Migrated.*PKCS12.*JKS.*ks.old6");
278 
279         Asserts.assertEQ(
280                 KeyStore.getInstance(
281                         new File("ks"), "changeit".toCharArray()).getType(),
282                 "PKCS12");
283 
284         Asserts.assertEQ(
285                 KeyStore.getInstance(
286                         new File("ks.old6"), "changeit".toCharArray()).getType(),
287                 "JKS");
288 
289         // One password prompt is enough for migration
290         kt0("-importkeystore -srckeystore ks -destkeystore ks", "changeit")
291                 .shouldMatch("original.*ks.old7");
292 
293         // But three if importing to a different keystore
294         rm("ks2");
295         kt0("-importkeystore -srckeystore ks -destkeystore ks2",
296                     "changeit")
297                 .shouldContain("Keystore password is too short");
298 
299         kt0("-importkeystore -srckeystore ks -destkeystore ks2",
300                 "changeit", "changeit", "changeit")
301                 .shouldContain("Importing keystore ks to ks2...")
302                 .shouldNotContain("original")
303                 .shouldNotContain("Migrated");
304     }
305 
checkImport()306     static void checkImport() throws Exception {
307 
308         saveStore();
309 
310         // add trusted cert
311 
312         // cert already in
313         kt("-importcert -alias d -file a.cert", "no")
314                 .shouldContain("Certificate already exists in keystore")
315                 .shouldContain("Warning")
316                 .shouldMatch("The input.*MD5withRSA.*risk")
317                 .shouldContain("Do you still want to add it?");
318         kt("-importcert -alias d -file a.cert -noprompt")
319                 .shouldContain("Warning")
320                 .shouldMatch("The input.*MD5withRSA.*risk")
321                 .shouldNotContain("[no]");
322 
323         // cert is self-signed
324         kt("-delete -alias a");
325         kt("-delete -alias d");
326         kt("-importcert -alias d -file a.cert", "no")
327                 .shouldContain("Warning")
328                 .shouldContain("MD5withRSA (weak)")
329                 .shouldMatch("The input.*MD5withRSA.*risk")
330                 .shouldContain("Trust this certificate?");
331         kt("-importcert -alias d -file a.cert -noprompt")
332                 .shouldContain("Warning")
333                 .shouldMatch("The input.*MD5withRSA.*risk")
334                 .shouldNotContain("[no]");
335 
336         // JDK-8177569: no warning for sigalg of trusted cert
337         String weakSigAlgCA = null;
338         KeyStore ks = KeyStoreUtil.getCacertsKeyStore();
339         if (ks != null) {
340             DisabledAlgorithmConstraints disabledCheck =
341                     new DisabledAlgorithmConstraints(
342                             DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
343             Set<CryptoPrimitive> sigPrimitiveSet = Collections
344                     .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
345 
346             for (String s : Collections.list(ks.aliases())) {
347                 if (ks.isCertificateEntry(s)) {
348                     X509Certificate c = (X509Certificate)ks.getCertificate(s);
349                     String sigAlg = c.getSigAlgName();
350                     if (!disabledCheck.permits(sigPrimitiveSet, sigAlg, null)) {
351                         weakSigAlgCA = sigAlg;
352                         Files.write(Paths.get("ca.cert"),
353                                 ks.getCertificate(s).getEncoded());
354                         break;
355                     }
356                 }
357             }
358         }
359         if (weakSigAlgCA != null) {
360             // The following 2 commands still have a warning on why not using
361             // the -cacerts option directly.
362             kt("-list -keystore " + KeyStoreUtil.getCacerts())
363                     .shouldNotContain("risk");
364             kt("-list -v -keystore " + KeyStoreUtil.getCacerts())
365                     .shouldNotContain("risk");
366 
367             // -printcert will always show warnings
368             kt("-printcert -file ca.cert")
369                     .shouldContain("name: " + weakSigAlgCA + " (weak)")
370                     .shouldContain("Warning")
371                     .shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
372             kt("-printcert -file ca.cert -trustcacerts") // -trustcacerts useless
373                     .shouldContain("name: " + weakSigAlgCA + " (weak)")
374                     .shouldContain("Warning")
375                     .shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
376 
377             // Importing with -trustcacerts ignore CA cert's sig alg
378             kt("-delete -alias d");
379             kt("-importcert -alias d -trustcacerts -file ca.cert", "no")
380                     .shouldContain("Certificate already exists in system-wide CA")
381                     .shouldNotContain("risk")
382                     .shouldContain("Do you still want to add it to your own keystore?");
383             kt("-importcert -alias d -trustcacerts -file ca.cert -noprompt")
384                     .shouldNotContain("risk")
385                     .shouldNotContain("[no]");
386 
387             // but not without -trustcacerts
388             kt("-delete -alias d");
389             kt("-importcert -alias d -file ca.cert", "no")
390                     .shouldContain("name: " + weakSigAlgCA + " (weak)")
391                     .shouldContain("Warning")
392                     .shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
393                     .shouldContain("Trust this certificate?");
394             kt("-importcert -alias d -file ca.cert -noprompt")
395                     .shouldContain("Warning")
396                     .shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
397                     .shouldNotContain("[no]");
398         }
399 
400         // a non self-signed weak cert
401         reStore();
402         certreq("b", "");
403         gencert("c-b", "");
404         kt("-importcert -alias d -file c-b.cert")   // weak only, no prompt
405                 .shouldContain("Warning")
406                 .shouldNotContain("512-bit RSA key (weak)")
407                 .shouldMatch("The input.*512-bit RSA key.*risk")
408                 .shouldNotContain("[no]");
409 
410         kt("-delete -alias b");
411         kt("-delete -alias c");
412         kt("-delete -alias d");
413 
414         kt("-importcert -alias d -file c-b.cert", "no") // weak and not trusted
415                 .shouldContain("Warning")
416                 .shouldContain("512-bit RSA key (weak)")
417                 .shouldMatch("The input.*512-bit RSA key.*risk")
418                 .shouldContain("Trust this certificate?");
419         kt("-importcert -alias d -file c-b.cert -noprompt")
420                 .shouldContain("Warning")
421                 .shouldMatch("The input.*512-bit RSA key.*risk")
422                 .shouldNotContain("[no]");
423 
424         // a non self-signed strong cert
425         reStore();
426         certreq("a", "");
427         gencert("c-a", "");
428         kt("-importcert -alias d -file c-a.cert") // trusted
429                 .shouldNotContain("Warning")
430                 .shouldNotContain("[no]");
431 
432         kt("-delete -alias a");
433         kt("-delete -alias c");
434         kt("-delete -alias d");
435 
436         kt("-importcert -alias d -file c-a.cert", "no") // not trusted
437                 .shouldNotContain("Warning")
438                 .shouldContain("Trust this certificate?");
439         kt("-importcert -alias d -file c-a.cert -noprompt")
440                 .shouldNotContain("Warning")
441                 .shouldNotContain("[no]");
442 
443         // install reply
444 
445         reStore();
446         certreq("c", "");
447         gencert("a-c", "");
448         kt("-importcert -alias c -file a-c.cert")
449                 .shouldContain("Warning")
450                 .shouldMatch("Issuer <a>.*MD5withRSA.*risk");
451 
452         // JDK-8177569: no warning for sigalg of trusted cert
453         reStore();
454         // Change a into a TrustedCertEntry
455         kt("-exportcert -alias a -file a.cert");
456         kt("-delete -alias a");
457         kt("-importcert -alias a -file a.cert -noprompt");
458         kt("-list -alias a -v")
459                 .shouldNotContain("weak")
460                 .shouldNotContain("Warning");
461         // This time a is trusted and no warning on its weak sig alg
462         kt("-importcert -alias c -file a-c.cert")
463                 .shouldNotContain("Warning");
464 
465         reStore();
466 
467         gencert("a-b", "");
468         gencert("b-c", "");
469 
470         // Full chain with root
471         cat("a-a-b-c.cert", "b-c.cert", "a-b.cert", "a.cert");
472         kt("-importcert -alias c -file a-a-b-c.cert")   // only weak
473                 .shouldContain("Warning")
474                 .shouldMatch("Reply #2 of 3.*512-bit RSA key.*risk")
475                 .shouldMatch("Reply #3 of 3.*MD5withRSA.*risk")
476                 .shouldNotContain("[no]");
477 
478         // Without root
479         cat("a-b-c.cert", "b-c.cert", "a-b.cert");
480         kt("-importcert -alias c -file a-b-c.cert")     // only weak
481                 .shouldContain("Warning")
482                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
483                 .shouldMatch("Issuer <a>.*MD5withRSA.*risk")
484                 .shouldNotContain("[no]");
485 
486         reStore();
487         gencert("b-a", "");
488 
489         kt("-importcert -alias a -file b-a.cert")
490                 .shouldContain("Warning")
491                 .shouldMatch("Issuer <b>.*512-bit RSA key.*risk")
492                 .shouldNotContain("[no]");
493 
494         kt("-importcert -alias a -file c-a.cert")
495                 .shouldNotContain("Warning");
496 
497         kt("-importcert -alias b -file c-b.cert")
498                 .shouldContain("Warning")
499                 .shouldMatch("The input.*512-bit RSA key.*risk")
500                 .shouldNotContain("[no]");
501 
502         reStore();
503         gencert("b-a", "");
504 
505         cat("c-b-a.cert", "b-a.cert", "c-b.cert");
506 
507         kt("-printcert -file c-b-a.cert")
508                 .shouldContain("Warning")
509                 .shouldMatch("The certificate #2 of 2.*512-bit RSA key.*risk");
510 
511         kt("-delete -alias b");
512 
513         kt("-importcert -alias a -file c-b-a.cert")
514                 .shouldContain("Warning")
515                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
516                 .shouldNotContain("[no]");
517 
518         kt("-delete -alias c");
519         kt("-importcert -alias a -file c-b-a.cert", "no")
520                 .shouldContain("Top-level certificate in reply:")
521                 .shouldContain("512-bit RSA key (weak)")
522                 .shouldContain("Warning")
523                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
524                 .shouldContain("Install reply anyway?");
525         kt("-importcert -alias a -file c-b-a.cert -noprompt")
526                 .shouldContain("Warning")
527                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
528                 .shouldNotContain("[no]");
529 
530         reStore();
531     }
532 
cat(String dest, String... src)533     private static void cat(String dest, String... src) throws IOException {
534         System.out.println("---------------------------------------------");
535         System.out.printf("$ cat ");
536 
537         ByteArrayOutputStream bout = new ByteArrayOutputStream();
538         for (String s : src) {
539             System.out.printf(s + " ");
540             bout.write(Files.readAllBytes(Paths.get(s)));
541         }
542         Files.write(Paths.get(dest), bout.toByteArray());
543         System.out.println("> " + dest);
544     }
545 
checkGenCRL(String alias, String options, String bad)546     static void checkGenCRL(String alias, String options, String bad) {
547 
548         OutputAnalyzer oa = kt("-gencrl -alias " + alias
549                 + " -id 1 -file " + alias + ".crl " + options);
550         if (bad == null) {
551             oa.shouldNotContain("Warning");
552         } else {
553             oa.shouldContain("Warning")
554                     .shouldMatch("The generated CRL.*" + bad + ".*risk");
555         }
556 
557         oa = kt("-printcrl -file " + alias + ".crl");
558         if (bad == null) {
559             oa.shouldNotContain("Warning")
560                     .shouldContain("Verified by " + alias + " in keystore")
561                     .shouldNotContain("(weak");
562         } else {
563             oa.shouldContain("Warning:")
564                     .shouldMatch("The CRL.*" + bad + ".*risk")
565                     .shouldContain("Verified by " + alias + " in keystore")
566                     .shouldContain(bad + " (weak)");
567         }
568     }
569 
checkCertReq( String alias, String options, String bad)570     static void checkCertReq(
571             String alias, String options, String bad) {
572 
573         OutputAnalyzer oa = certreq(alias, options);
574         if (bad == null) {
575             oa.shouldNotContain("Warning");
576         } else {
577             oa.shouldContain("Warning")
578                     .shouldMatch("The generated certificate request.*" + bad + ".*risk");
579         }
580 
581         oa = kt("-printcertreq -file " + alias + ".req");
582         if (bad == null) {
583             oa.shouldNotContain("Warning")
584                     .shouldNotContain("(weak)");
585         } else {
586             oa.shouldContain("Warning")
587                     .shouldMatch("The certificate request.*" + bad + ".*risk")
588                     .shouldContain(bad + " (weak)");
589         }
590     }
591 
checkGenKeyPair( String alias, String options, String bad)592     static void checkGenKeyPair(
593             String alias, String options, String bad) {
594 
595         OutputAnalyzer oa = genkeypair(alias, options);
596         if (bad == null) {
597             oa.shouldNotContain("Warning");
598         } else {
599             oa.shouldContain("Warning")
600                     .shouldMatch("The generated certificate.*" + bad + ".*risk");
601         }
602 
603         oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
604         if (bad == null) {
605             oa.shouldNotContain("Warning");
606         } else {
607             oa.shouldContain("Warning")
608                     .shouldMatch("The certificate.*" + bad + ".*risk");
609         }
610 
611         oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert");
612         if (bad == null) {
613             oa.shouldNotContain("Warning");
614         } else {
615             oa.shouldContain("Warning")
616                     .shouldMatch("The certificate.*" + bad + ".*risk");
617         }
618 
619         oa = kt("-printcert -rfc -file " + alias + ".cert");
620         if (bad == null) {
621             oa.shouldNotContain("Warning");
622         } else {
623             oa.shouldContain("Warning")
624                     .shouldMatch("The certificate.*" + bad + ".*risk");
625         }
626 
627         oa = kt("-list -alias " + alias);
628         if (bad == null) {
629             oa.shouldNotContain("Warning");
630         } else {
631             oa.shouldContain("Warning")
632                     .shouldMatch("The certificate.*" + bad + ".*risk");
633         }
634 
635         // With cert content
636 
637         oa = kt("-printcert -file " + alias + ".cert");
638         if (bad == null) {
639             oa.shouldNotContain("Warning");
640         } else {
641             oa.shouldContain("Warning")
642                     .shouldContain(bad + " (weak)")
643                     .shouldMatch("The certificate.*" + bad + ".*risk");
644         }
645 
646         oa = kt("-list -v -alias " + alias);
647         if (bad == null) {
648             oa.shouldNotContain("Warning");
649         } else {
650             oa.shouldContain("Warning")
651                     .shouldContain(bad + " (weak)")
652                     .shouldMatch("The certificate.*" + bad + ".*risk");
653         }
654     }
655 
656     // This is slow, but real keytool process is launched.
kt1(String cmd, String... input)657     static OutputAnalyzer kt1(String cmd, String... input) {
658         cmd = "-keystore ks -storepass changeit " +
659                 "-keypass changeit " + cmd;
660         System.out.println("---------------------------------------------");
661         try {
662             SecurityTools.setResponse(input);
663             return SecurityTools.keytool(cmd);
664         } catch (Throwable e) {
665             throw new RuntimeException(e);
666         }
667     }
668 
kt(String cmd, String... input)669     static OutputAnalyzer kt(String cmd, String... input) {
670         return kt0("-keystore ks -storepass changeit " +
671                 "-keypass changeit " + cmd, input);
672     }
673 
674     // Fast keytool execution by directly calling its main() method
kt0(String cmd, String... input)675     static OutputAnalyzer kt0(String cmd, String... input) {
676         PrintStream out = System.out;
677         PrintStream err = System.err;
678         InputStream ins = System.in;
679         ByteArrayOutputStream bout = new ByteArrayOutputStream();
680         ByteArrayOutputStream berr = new ByteArrayOutputStream();
681         boolean succeed = true;
682         String sout;
683         String serr;
684         try {
685             System.out.println("---------------------------------------------");
686             System.out.println("$ keytool " + cmd);
687             System.out.println();
688             String feed = "";
689             if (input.length > 0) {
690                 feed = Stream.of(input).collect(Collectors.joining("\n")) + "\n";
691             }
692             System.setIn(new ByteArrayInputStream(feed.getBytes()));
693             System.setOut(new PrintStream(bout));
694             System.setErr(new PrintStream(berr));
695             sun.security.tools.keytool.Main.main(
696                     cmd.trim().split("\\s+"));
697         } catch (Exception e) {
698             // Might be a normal exception when -debug is on or
699             // SecurityException (thrown by jtreg) when System.exit() is called
700             if (!(e instanceof SecurityException)) {
701                 e.printStackTrace();
702             }
703             succeed = false;
704         } finally {
705             System.setOut(out);
706             System.setErr(err);
707             System.setIn(ins);
708             sout = new String(bout.toByteArray());
709             serr = new String(berr.toByteArray());
710             System.out.println("STDOUT:\n" + sout + "\nSTDERR:\n" + serr);
711         }
712         if (!succeed) {
713             throw new RuntimeException();
714         }
715         return new OutputAnalyzer(sout, serr);
716     }
717 
importkeystore(String src, String dest, String options)718     static OutputAnalyzer importkeystore(String src, String dest,
719                                          String options) {
720         return kt0("-importkeystore "
721                 + "-srckeystore " + src + " -destkeystore " + dest
722                 + " -srcstorepass changeit -deststorepass changeit " + options);
723     }
724 
genkeypair(String alias, String options)725     static OutputAnalyzer genkeypair(String alias, String options) {
726         return kt("-genkeypair -alias " + alias + " -dname CN=" + alias
727                 + " -storetype PKCS12 " + options);
728     }
729 
certreq(String alias, String options)730     static OutputAnalyzer certreq(String alias, String options) {
731         return kt("-certreq -alias " + alias
732                 + " -file " + alias + ".req " + options);
733     }
734 
exportcert(String alias)735     static OutputAnalyzer exportcert(String alias) {
736         return kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
737     }
738 
gencert(String relation, String options)739     static OutputAnalyzer gencert(String relation, String options) {
740         int pos = relation.indexOf("-");
741         String issuer = relation.substring(0, pos);
742         String subject = relation.substring(pos + 1);
743         return kt(" -gencert -alias " + issuer + " -infile " + subject
744                 + ".req -outfile " + relation + ".cert " + options);
745     }
746 
saveStore()747     static void saveStore() throws IOException {
748         System.out.println("---------------------------------------------");
749         System.out.println("$ cp ks ks2");
750         Files.copy(Paths.get("ks"), Paths.get("ks2"),
751                 StandardCopyOption.REPLACE_EXISTING);
752     }
753 
reStore()754     static void reStore() throws IOException {
755         System.out.println("---------------------------------------------");
756         System.out.println("$ cp ks2 ks");
757         Files.copy(Paths.get("ks2"), Paths.get("ks"),
758                 StandardCopyOption.REPLACE_EXISTING);
759     }
760 
rm(String s)761     static void rm(String s) throws IOException {
762         System.out.println("---------------------------------------------");
763         System.out.println("$ rm " + s);
764         Files.deleteIfExists(Paths.get(s));
765     }
766 }
767