1 /*
2  * Copyright (c) 2017, 2020, 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 8172404
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         // Tests for "disabled" algorithms
74         // -genkeypair, and -printcert, -list -alias, -exportcert
75         // (w/ different formats)
76         checkDisabledGenKeyPair("a", "-keyalg RSA -sigalg MD5withRSA", "MD5withRSA");
77         checkDisabledGenKeyPair("b", "-keyalg RSA -keysize 512", "512-bit RSA key");
78         checkDisabledGenKeyPair("c", "-keyalg RSA", null);
79 
80         kt("-list")
81                 .shouldContain("Warning:")
82                 .shouldMatch("<a>.*MD5withRSA.*is disabled")
83                 .shouldMatch("<b>.*512-bit RSA key.*is disabled");
84         kt("-list -v")
85                 .shouldContain("Warning:")
86                 .shouldMatch("<a>.*MD5withRSA.*is disabled")
87                 .shouldContain("MD5withRSA (disabled)")
88                 .shouldMatch("<b>.*512-bit RSA key.*is disabled")
89                 .shouldContain("512-bit RSA key (disabled)");
90 
91         // Multiple warnings for multiple cert in -printcert
92         // or -list or -exportcert
93 
94         // -certreq, -printcertreq, -gencert
95         checkDisabledCertReq("a", "", null);
96         gencert("c-a", "")
97                 .shouldNotContain("Warning"); // new sigalg is not weak
98         gencert("c-a", "-sigalg MD2withRSA")
99                 .shouldContain("Warning:")
100                 .shouldMatch("The generated certificate.*MD2withRSA.*is disabled");
101 
102         checkDisabledCertReq("a", "-sigalg MD5withRSA", "MD5withRSA");
103         gencert("c-a", "")
104                 .shouldContain("Warning:")
105                 .shouldMatch("The certificate request.*MD5withRSA.*is disabled");
106         gencert("c-a", "-sigalg MD2withRSA")
107                 .shouldContain("Warning:")
108                 .shouldMatch("The certificate request.*MD5withRSA.*is disabled")
109                 .shouldMatch("The generated certificate.*MD2withRSA.*is disabled");
110 
111         checkDisabledCertReq("b", "", "512-bit RSA key");
112         gencert("c-b", "")
113                 .shouldContain("Warning:")
114                 .shouldMatch("The certificate request.*512-bit RSA key.*is disabled")
115                 .shouldMatch("The generated certificate.*512-bit RSA key.*is disabled");
116 
117         checkDisabledCertReq("c", "", null);
118         gencert("a-c", "")
119                 .shouldContain("Warning:")
120                 .shouldMatch("The issuer.*MD5withRSA.*is disabled");
121 
122         // but the new cert is not weak
123         kt("-printcert -file a-c.cert")
124                 .shouldNotContain("Warning")
125                 .shouldNotContain("(disabled)");
126 
127         gencert("b-c", "")
128                 .shouldContain("Warning:")
129                 .shouldMatch("The issuer.*512-bit RSA key.*is disabled");
130 
131         // -importcert
132         checkImport();
133 
134         // -importkeystore
135         checkImportKeyStore();
136 
137         // -gencrl, -printcrl
138 
139         checkDisabledGenCRL("a", "", null);
140         checkDisabledGenCRL("a", "-sigalg MD5withRSA", "MD5withRSA");
141         checkDisabledGenCRL("b", "", "512-bit RSA key");
142         checkDisabledGenCRL("c", "", null);
143 
144         kt("-delete -alias b");
145         kt("-printcrl -file b.crl")
146                 .shouldContain("WARNING: not verified");
147 
148         jksTypeCheck();
149 
150         checkInplaceImportKeyStore();
151 
152         rm("ks");
153 
154         // Tests for "legacy" algorithms
155         // -genkeypair, and -printcert, -list -alias, -exportcert
156         // (w/ different formats)
157         checkWeakGenKeyPair("x", "-keyalg RSA -sigalg SHA1withRSA", "SHA1withRSA");
158         checkWeakGenKeyPair("y", "-keyalg RSA -keysize 1024", "1024-bit RSA key");
159         checkWeakGenKeyPair("z", "-keyalg RSA", null);
160 
161         kt("-list")
162                 .shouldContain("Warning:")
163                 .shouldMatch("<x>.*SHA1withRSA.*will be disabled")
164                 .shouldMatch("<y>.*1024-bit RSA key.*will be disabled");
165         kt("-list -v")
166                 .shouldContain("Warning:")
167                 .shouldMatch("<x>.*SHA1withRSA.*will be disabled")
168                 .shouldContain("SHA1withRSA (weak)")
169                 .shouldMatch("<y>.*1024-bit RSA key.*will be disabled")
170                 .shouldContain("1024-bit RSA key (weak)");
171 
172         // Multiple warnings for multiple cert in -printcert
173         // or -list or -exportcert
174 
175         // -certreq, -printcertreq, -gencert
176         checkWeakCertReq("x", "", null);
177         gencert("z-x", "")
178                 .shouldNotContain("Warning"); // new sigalg is not weak
179         gencert("z-x", "-sigalg SHA1withRSA")
180                 .shouldContain("Warning:")
181                 .shouldMatch("The generated certificate.*SHA1withRSA.*will be disabled");
182 
183         checkWeakCertReq("x", "-sigalg SHA1withRSA", "SHA1withRSA");
184         gencert("z-x", "")
185                 .shouldContain("Warning:")
186                 .shouldMatch("The certificate request.*SHA1withRSA.*will be disabled");
187         gencert("z-x", "-sigalg SHA1withRSA")
188                 .shouldContain("Warning:")
189                 .shouldMatch("The certificate request.*SHA1withRSA.*will be disabled")
190                 .shouldMatch("The generated certificate.*SHA1withRSA.*will be disabled");
191 
192         checkWeakCertReq("y", "", "1024-bit RSA key");
193         gencert("z-y", "")
194                 .shouldContain("Warning:")
195                 .shouldMatch("The certificate request.*1024-bit RSA key.*will be disabled")
196                 .shouldMatch("The generated certificate.*1024-bit RSA key.*will be disabled");
197 
198         checkWeakCertReq("z", "", null);
199         gencert("x-z", "")
200                 .shouldContain("Warning:")
201                 .shouldMatch("The issuer.*SHA1withRSA.*will be disabled");
202 
203         // but the new cert is not weak
204         kt("-printcert -file x-z.cert")
205                 .shouldNotContain("Warning")
206                 .shouldNotContain("weak");
207 
208         gencert("y-z", "")
209                 .shouldContain("Warning:")
210                 .shouldMatch("The issuer.*1024-bit RSA key.*will be disabled");
211 
212         // -gencrl, -printcrl
213         checkWeakGenCRL("x", "", null);
214         checkWeakGenCRL("x", "-sigalg SHA1withRSA", "SHA1withRSA");
215         checkWeakGenCRL("y", "", "1024-bit RSA key");
216         checkWeakGenCRL("z", "", null);
217 
218         kt("-delete -alias y");
219         kt("-printcrl -file y.crl")
220                 .shouldContain("WARNING: not verified");
221 
222         jksTypeCheck();
223     }
224 
jksTypeCheck()225     static void jksTypeCheck() throws Exception {
226 
227         // No warning for cacerts, all certs
228         kt0("-cacerts -list -storepass changeit")
229                 .shouldNotContain("proprietary format");
230 
231         rm("ks");
232         rm("ks2");
233 
234         kt("-genkeypair -keyalg DSA -alias a -dname CN=A")
235                 .shouldNotContain("Warning:");
236         kt("-list")
237                 .shouldNotContain("Warning:");
238         kt("-list -storetype jks") // no warning if PKCS12 used as JKS
239                 .shouldNotContain("Warning:");
240         kt("-exportcert -alias a -file a.crt")
241                 .shouldNotContain("Warning:");
242 
243         // warn if migrating to JKS
244         importkeystore("ks", "ks2", "-deststoretype jks")
245                 .shouldContain("JKS keystore uses a proprietary format");
246 
247         rm("ks");
248         rm("ks2");
249         rm("ks3");
250 
251         // no warning if all certs
252         kt("-importcert -alias b -file a.crt -storetype jks -noprompt")
253                 .shouldNotContain("Warning:");
254         kt("-genkeypair -keyalg DSA -alias a -dname CN=A")
255                 .shouldContain("JKS keystore uses a proprietary format");
256         kt("-list")
257                 .shouldContain("JKS keystore uses a proprietary format");
258         kt("-list -storetype pkcs12") // warn if JKS used as PKCS12
259                 .shouldContain("JKS keystore uses a proprietary format");
260         kt("-exportcert -alias a -file a.crt")
261                 .shouldContain("JKS keystore uses a proprietary format");
262         kt("-printcert -file a.crt") // no warning if keystore not touched
263                 .shouldNotContain("Warning:");
264         kt("-certreq -alias a -file a.req")
265                 .shouldContain("JKS keystore uses a proprietary format");
266         kt("-printcertreq -file a.req") // no warning if keystore not touched
267                 .shouldNotContain("Warning:");
268 
269         // No warning if migrating from JKS
270         importkeystore("ks", "ks2", "")
271                 .shouldNotContain("Warning:");
272 
273         importkeystore("ks", "ks3", "-deststoretype pkcs12")
274                 .shouldNotContain("Warning:");
275 
276         rm("ks");
277 
278         kt("-genkeypair -keyalg DSA -alias a -dname CN=A -storetype jceks")
279                 .shouldContain("JCEKS keystore uses a proprietary format");
280         kt("-list")
281                 .shouldContain("JCEKS keystore uses a proprietary format");
282         kt("-importcert -alias b -file a.crt -noprompt")
283                 .shouldContain("JCEKS keystore uses a proprietary format");
284         kt("-exportcert -alias a -file a.crt")
285                 .shouldContain("JCEKS keystore uses a proprietary format");
286         kt("-printcert -file a.crt")
287                 .shouldNotContain("Warning:");
288         kt("-certreq -alias a -file a.req")
289                 .shouldContain("JCEKS keystore uses a proprietary format");
290         kt("-printcertreq -file a.req")
291                 .shouldNotContain("Warning:");
292         kt("-genseckey -alias c -keyalg AES -keysize 128")
293                 .shouldContain("JCEKS keystore uses a proprietary format");
294     }
295 
checkImportKeyStore()296     static void checkImportKeyStore() throws Exception {
297 
298         rm("ks2");
299         rm("ks3");
300 
301         importkeystore("ks", "ks2", "")
302                 .shouldContain("3 entries successfully imported")
303                 .shouldContain("Warning")
304                 .shouldMatch("<b>.*512-bit RSA key.*is disabled")
305                 .shouldMatch("<a>.*MD5withRSA.*is disabled");
306 
307         importkeystore("ks", "ks3", "-srcalias a")
308                 .shouldContain("Warning")
309                 .shouldMatch("<a>.*MD5withRSA.*is disabled");
310     }
311 
checkInplaceImportKeyStore()312     static void checkInplaceImportKeyStore() throws Exception {
313 
314         rm("ks");
315         genkeypair("a", "-keyalg DSA");
316 
317         // Same type backup
318         importkeystore("ks", "ks", "")
319                 .shouldContain("Warning:")
320                 .shouldMatch("original.*ks.old");
321 
322         importkeystore("ks", "ks", "")
323                 .shouldContain("Warning:")
324                 .shouldMatch("original.*ks.old2");
325 
326         importkeystore("ks", "ks", "-srcstoretype jks") // it knows real type
327                 .shouldContain("Warning:")
328                 .shouldMatch("original.*ks.old3");
329 
330         String cPath = new File("ks").getCanonicalPath();
331 
332         importkeystore("ks", cPath, "")
333                 .shouldContain("Warning:")
334                 .shouldMatch("original.*ks.old4");
335 
336         // Migration
337         importkeystore("ks", "ks", "-deststoretype jks")
338                 .shouldContain("Warning:")
339                 .shouldContain("JKS keystore uses a proprietary format")
340                 .shouldMatch("Migrated.*JKS.*PKCS12.*ks.old5");
341 
342         Asserts.assertEQ(
343                 KeyStore.getInstance(
344                         new File("ks"), "changeit".toCharArray()).getType(),
345                 "JKS");
346 
347         importkeystore("ks", "ks", "-srcstoretype PKCS12")
348                 .shouldContain("Warning:")
349                 .shouldNotContain("proprietary format")
350                 .shouldMatch("Migrated.*PKCS12.*JKS.*ks.old6");
351 
352         Asserts.assertEQ(
353                 KeyStore.getInstance(
354                         new File("ks"), "changeit".toCharArray()).getType(),
355                 "PKCS12");
356 
357         Asserts.assertEQ(
358                 KeyStore.getInstance(
359                         new File("ks.old6"), "changeit".toCharArray()).getType(),
360                 "JKS");
361 
362         // One password prompt is enough for migration
363         kt0("-importkeystore -srckeystore ks -destkeystore ks", "changeit")
364                 .shouldMatch("original.*ks.old7");
365 
366         // But three if importing to a different keystore
367         rm("ks2");
368         kt0("-importkeystore -srckeystore ks -destkeystore ks2",
369                     "changeit")
370                 .shouldContain("Keystore password is too short");
371 
372         kt0("-importkeystore -srckeystore ks -destkeystore ks2",
373                 "changeit", "changeit", "changeit")
374                 .shouldContain("Importing keystore ks to ks2...")
375                 .shouldNotContain("original")
376                 .shouldNotContain("Migrated");
377     }
378 
checkImport()379     static void checkImport() throws Exception {
380 
381         saveStore();
382 
383         // add trusted cert
384 
385         // cert already in
386         kt("-importcert -alias d -file a.cert", "no")
387                 .shouldContain("Certificate already exists in keystore")
388                 .shouldContain("Warning")
389                 .shouldMatch("The input.*MD5withRSA.*is disabled")
390                 .shouldContain("Do you still want to add it?");
391         kt("-importcert -alias d -file a.cert -noprompt")
392                 .shouldContain("Warning")
393                 .shouldMatch("The input.*MD5withRSA.*is disabled")
394                 .shouldNotContain("[no]");
395 
396         // cert is self-signed
397         kt("-delete -alias a");
398         kt("-delete -alias d");
399         kt("-importcert -alias d -file a.cert", "no")
400                 .shouldContain("Warning")
401                 .shouldContain("MD5withRSA (disabled)")
402                 .shouldMatch("The input.*MD5withRSA.*is disabled")
403                 .shouldContain("Trust this certificate?");
404         kt("-importcert -alias d -file a.cert -noprompt")
405                 .shouldContain("Warning")
406                 .shouldMatch("The input.*MD5withRSA.*is disabled")
407                 .shouldNotContain("[no]");
408 
409         // JDK-8177569: no warning for sigalg of trusted cert
410         String weakSigAlgCA = null;
411         KeyStore ks = KeyStoreUtil.getCacertsKeyStore();
412         if (ks != null) {
413             DisabledAlgorithmConstraints disabledCheck =
414                     new DisabledAlgorithmConstraints(
415                             DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
416             Set<CryptoPrimitive> sigPrimitiveSet = Collections
417                     .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
418 
419             for (String s : Collections.list(ks.aliases())) {
420                 if (ks.isCertificateEntry(s)) {
421                     X509Certificate c = (X509Certificate)ks.getCertificate(s);
422                     String sigAlg = c.getSigAlgName();
423                     if (!disabledCheck.permits(sigPrimitiveSet, sigAlg, null)) {
424                         weakSigAlgCA = sigAlg;
425                         Files.write(Paths.get("ca.cert"),
426                                 ks.getCertificate(s).getEncoded());
427                         break;
428                     }
429                 }
430             }
431         }
432         if (weakSigAlgCA != null) {
433             // The following 2 commands still have a warning on why not using
434             // the -cacerts option directly.
435             kt("-list -keystore " + KeyStoreUtil.getCacerts())
436                     .shouldNotMatch("signature algorithm.*risk");
437             kt("-list -v -keystore " + KeyStoreUtil.getCacerts())
438                     .shouldNotMatch("signature algorithm.*risk");
439 
440             // -printcert will always show warnings
441             kt("-printcert -file ca.cert")
442                     .shouldContain("name: " + weakSigAlgCA + " (disabled)")
443                     .shouldContain("Warning")
444                     .shouldMatch("The certificate.*" + weakSigAlgCA + ".*is disabled");
445             kt("-printcert -file ca.cert -trustcacerts") // -trustcacerts useless
446                     .shouldContain("name: " + weakSigAlgCA + " (disabled)")
447                     .shouldContain("Warning")
448                     .shouldMatch("The certificate.*" + weakSigAlgCA + ".*is disabled");
449 
450             // Importing with -trustcacerts ignore CA cert's sig alg
451             kt("-delete -alias d");
452             kt("-importcert -alias d -trustcacerts -file ca.cert", "no")
453                     .shouldContain("Certificate already exists in system-wide CA")
454                     .shouldNotMatch("signature algorithm.*risk")
455                     .shouldContain("Do you still want to add it to your own keystore?");
456             kt("-importcert -alias d -trustcacerts -file ca.cert -noprompt")
457                     .shouldNotMatch("signature algorithm.*risk")
458                     .shouldNotContain("[no]");
459 
460             // but not without -trustcacerts
461             kt("-delete -alias d");
462             kt("-importcert -alias d -file ca.cert", "no")
463                     .shouldContain("name: " + weakSigAlgCA + " (disabled)")
464                     .shouldContain("Warning")
465                     .shouldMatch("The input.*" + weakSigAlgCA + ".*is disabled")
466                     .shouldContain("Trust this certificate?");
467             kt("-importcert -alias d -file ca.cert -noprompt")
468                     .shouldContain("Warning")
469                     .shouldMatch("The input.*" + weakSigAlgCA + ".*is disabled")
470                     .shouldNotContain("[no]");
471         }
472 
473         // a non self-signed weak cert
474         reStore();
475         certreq("b", "");
476         gencert("c-b", "");
477         kt("-importcert -alias d -file c-b.cert")   // weak only, no prompt
478                 .shouldContain("Warning")
479                 .shouldNotContain("512-bit RSA key (disabled)")
480                 .shouldMatch("The input.*512-bit RSA key.*is disabled")
481                 .shouldNotContain("[no]");
482 
483         kt("-delete -alias b");
484         kt("-delete -alias c");
485         kt("-delete -alias d");
486 
487         kt("-importcert -alias d -file c-b.cert", "no") // weak and not trusted
488                 .shouldContain("Warning")
489                 .shouldContain("512-bit RSA key (disabled)")
490                 .shouldMatch("The input.*512-bit RSA key.*is disabled")
491                 .shouldContain("Trust this certificate?");
492         kt("-importcert -alias d -file c-b.cert -noprompt")
493                 .shouldContain("Warning")
494                 .shouldMatch("The input.*512-bit RSA key.*is disabled")
495                 .shouldNotContain("[no]");
496 
497         // a non self-signed strong cert
498         reStore();
499         certreq("a", "");
500         gencert("c-a", "");
501         kt("-importcert -alias d -file c-a.cert") // trusted
502                 .shouldNotContain("Warning")
503                 .shouldNotContain("[no]");
504 
505         kt("-delete -alias a");
506         kt("-delete -alias c");
507         kt("-delete -alias d");
508 
509         kt("-importcert -alias d -file c-a.cert", "no") // not trusted
510                 .shouldNotContain("Warning")
511                 .shouldContain("Trust this certificate?");
512         kt("-importcert -alias d -file c-a.cert -noprompt")
513                 .shouldNotContain("Warning")
514                 .shouldNotContain("[no]");
515 
516         // install reply
517 
518         reStore();
519         certreq("c", "");
520         gencert("a-c", "");
521         kt("-importcert -alias c -file a-c.cert")
522                 .shouldContain("Warning")
523                 .shouldMatch("Issuer <a>.*MD5withRSA.*is disabled");
524 
525         // JDK-8177569: no warning for sigalg of trusted cert
526         reStore();
527         // Change a into a TrustedCertEntry
528         kt("-exportcert -alias a -file a.cert");
529         kt("-delete -alias a");
530         kt("-importcert -alias a -file a.cert -noprompt");
531         kt("-list -alias a -v")
532                 .shouldNotContain("disabled")
533                 .shouldNotContain("Warning");
534         // This time a is trusted and no warning on its weak sig alg
535         kt("-importcert -alias c -file a-c.cert")
536                 .shouldNotContain("Warning");
537 
538         reStore();
539 
540         gencert("a-b", "");
541         gencert("b-c", "");
542 
543         // Full chain with root
544         cat("a-a-b-c.cert", "b-c.cert", "a-b.cert", "a.cert");
545         kt("-importcert -alias c -file a-a-b-c.cert")   // only weak
546                 .shouldContain("Warning")
547                 .shouldMatch("Reply #2 of 3.*512-bit RSA key.*is disabled")
548                 .shouldMatch("Reply #3 of 3.*MD5withRSA.*is disabled")
549                 .shouldNotContain("[no]");
550 
551         // Without root
552         cat("a-b-c.cert", "b-c.cert", "a-b.cert");
553         kt("-importcert -alias c -file a-b-c.cert")     // only weak
554                 .shouldContain("Warning")
555                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled")
556                 .shouldMatch("Issuer <a>.*MD5withRSA.*is disabled")
557                 .shouldNotContain("[no]");
558 
559         reStore();
560         gencert("b-a", "");
561 
562         kt("-importcert -alias a -file b-a.cert")
563                 .shouldContain("Warning")
564                 .shouldMatch("Issuer <b>.*512-bit RSA key.*is disabled")
565                 .shouldNotContain("[no]");
566 
567         kt("-importcert -alias a -file c-a.cert")
568                 .shouldNotContain("Warning");
569 
570         kt("-importcert -alias b -file c-b.cert")
571                 .shouldContain("Warning")
572                 .shouldMatch("The input.*512-bit RSA key.*is disabled")
573                 .shouldNotContain("[no]");
574 
575         reStore();
576         gencert("b-a", "");
577 
578         cat("c-b-a.cert", "b-a.cert", "c-b.cert");
579 
580         kt("-printcert -file c-b-a.cert")
581                 .shouldContain("Warning")
582                 .shouldMatch("The certificate #2 of 2.*512-bit RSA key.*is disabled");
583 
584         kt("-delete -alias b");
585 
586         kt("-importcert -alias a -file c-b-a.cert")
587                 .shouldContain("Warning")
588                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled")
589                 .shouldNotContain("[no]");
590 
591         kt("-delete -alias c");
592         kt("-importcert -alias a -file c-b-a.cert", "no")
593                 .shouldContain("Top-level certificate in reply:")
594                 .shouldContain("512-bit RSA key (disabled)")
595                 .shouldContain("Warning")
596                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled")
597                 .shouldContain("Install reply anyway?");
598         kt("-importcert -alias a -file c-b-a.cert -noprompt")
599                 .shouldContain("Warning")
600                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled")
601                 .shouldNotContain("[no]");
602 
603         reStore();
604     }
605 
cat(String dest, String... src)606     private static void cat(String dest, String... src) throws IOException {
607         System.out.println("---------------------------------------------");
608         System.out.printf("$ cat ");
609 
610         ByteArrayOutputStream bout = new ByteArrayOutputStream();
611         for (String s : src) {
612             System.out.printf(s + " ");
613             bout.write(Files.readAllBytes(Paths.get(s)));
614         }
615         Files.write(Paths.get(dest), bout.toByteArray());
616         System.out.println("> " + dest);
617     }
618 
checkDisabledGenCRL(String alias, String options, String bad)619     static void checkDisabledGenCRL(String alias, String options, String bad) {
620 
621         OutputAnalyzer oa = kt("-gencrl -alias " + alias
622                 + " -id 1 -file " + alias + ".crl " + options);
623         if (bad == null) {
624             oa.shouldNotContain("Warning");
625         } else {
626             oa.shouldContain("Warning")
627                     .shouldMatch("The generated CRL.*" + bad + ".*is disabled");
628         }
629 
630         oa = kt("-printcrl -file " + alias + ".crl");
631         if (bad == null) {
632             oa.shouldNotContain("Warning")
633                     .shouldContain("Verified by " + alias + " in keystore")
634                     .shouldNotContain("(disabled");
635         } else {
636             oa.shouldContain("Warning:")
637                     .shouldMatch("The CRL.*" + bad + ".*is disabled")
638                     .shouldContain("Verified by " + alias + " in keystore")
639                     .shouldContain(bad + " (disabled)");
640         }
641     }
642 
checkDisabledCertReq( String alias, String options, String bad)643     static void checkDisabledCertReq(
644             String alias, String options, String bad) {
645 
646         OutputAnalyzer oa = certreq(alias, options);
647         if (bad == null) {
648             oa.shouldNotContain("Warning");
649         } else {
650             oa.shouldContain("Warning")
651                     .shouldMatch("The generated certificate request.*" + bad + ".*is disabled");
652         }
653 
654         oa = kt("-printcertreq -file " + alias + ".req");
655         if (bad == null) {
656             oa.shouldNotContain("Warning")
657                     .shouldNotContain("(disabled)");
658         } else {
659             oa.shouldContain("Warning")
660                     .shouldMatch("The certificate request.*" + bad + ".*is disabled")
661                     .shouldContain(bad + " (disabled)");
662         }
663     }
664 
checkDisabledGenKeyPair( String alias, String options, String bad)665     static void checkDisabledGenKeyPair(
666             String alias, String options, String bad) {
667 
668         OutputAnalyzer oa = genkeypair(alias, options);
669         if (bad == null) {
670             oa.shouldNotContain("Warning");
671         } else {
672             oa.shouldContain("Warning")
673                     .shouldMatch("The generated certificate.*" + bad + ".*is disabled");
674         }
675 
676         oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
677         if (bad == null) {
678             oa.shouldNotContain("Warning");
679         } else {
680             oa.shouldContain("Warning")
681                     .shouldMatch("The certificate.*" + bad + ".*is disabled");
682         }
683 
684         oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert");
685         if (bad == null) {
686             oa.shouldNotContain("Warning");
687         } else {
688             oa.shouldContain("Warning")
689                     .shouldMatch("The certificate.*" + bad + ".*is disabled");
690         }
691 
692         oa = kt("-printcert -rfc -file " + alias + ".cert");
693         if (bad == null) {
694             oa.shouldNotContain("Warning");
695         } else {
696             oa.shouldContain("Warning")
697                     .shouldMatch("The certificate.*" + bad + ".*is disabled");
698         }
699 
700         oa = kt("-list -alias " + alias);
701         if (bad == null) {
702             oa.shouldNotContain("Warning");
703         } else {
704             oa.shouldContain("Warning")
705                     .shouldMatch("The certificate.*" + bad + ".*is disabled");
706         }
707 
708         // With cert content
709 
710         oa = kt("-printcert -file " + alias + ".cert");
711         if (bad == null) {
712             oa.shouldNotContain("Warning");
713         } else {
714             oa.shouldContain("Warning")
715                     .shouldContain(bad + " (disabled)")
716                     .shouldMatch("The certificate.*" + bad + ".*is disabled");
717         }
718 
719         oa = kt("-list -v -alias " + alias);
720         if (bad == null) {
721             oa.shouldNotContain("Warning");
722         } else {
723             oa.shouldContain("Warning")
724                     .shouldContain(bad + " (disabled)")
725                     .shouldMatch("The certificate.*" + bad + ".*is disabled");
726         }
727     }
728 
checkWeakGenKeyPair( String alias, String options, String bad)729     static void checkWeakGenKeyPair(
730             String alias, String options, String bad) {
731 
732         OutputAnalyzer oa = genkeypair(alias, options);
733         if (bad == null) {
734             oa.shouldNotContain("Warning");
735         } else {
736             oa.shouldContain("Warning")
737                     .shouldMatch("The generated certificate.*" + bad + ".*will be disabled");
738         }
739 
740         oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
741         if (bad == null) {
742             oa.shouldNotContain("Warning");
743         } else {
744             oa.shouldContain("Warning")
745                     .shouldMatch("The certificate.*" + bad + ".*will be disabled");
746         }
747 
748         oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert");
749         if (bad == null) {
750             oa.shouldNotContain("Warning");
751         } else {
752             oa.shouldContain("Warning")
753                     .shouldMatch("The certificate.*" + bad + ".*will be disabled");
754         }
755 
756         oa = kt("-printcert -rfc -file " + alias + ".cert");
757         if (bad == null) {
758             oa.shouldNotContain("Warning");
759         } else {
760             oa.shouldContain("Warning")
761                     .shouldMatch("The certificate.*" + bad + ".*will be disabled");
762         }
763 
764         oa = kt("-list -alias " + alias);
765         if (bad == null) {
766             oa.shouldNotContain("Warning");
767         } else {
768             oa.shouldContain("Warning")
769                     .shouldMatch("The certificate.*" + bad + ".*will be disabled");
770         }
771 
772         // With cert content
773 
774         oa = kt("-printcert -file " + alias + ".cert");
775         if (bad == null) {
776             oa.shouldNotContain("Warning");
777         } else {
778             oa.shouldContain("Warning")
779                     .shouldContain(bad + " (weak)")
780                     .shouldMatch("The certificate.*" + bad + ".*will be disabled");
781         }
782 
783         oa = kt("-list -v -alias " + alias);
784         if (bad == null) {
785             oa.shouldNotContain("Warning");
786         } else {
787             oa.shouldContain("Warning")
788                     .shouldContain(bad + " (weak)")
789                     .shouldMatch("The certificate.*" + bad + ".*will be disabled");
790         }
791     }
792 
793 
checkWeakGenCRL(String alias, String options, String bad)794     static void checkWeakGenCRL(String alias, String options, String bad) {
795 
796         OutputAnalyzer oa = kt("-gencrl -alias " + alias
797                 + " -id 1 -file " + alias + ".crl " + options);
798         if (bad == null) {
799             oa.shouldNotContain("Warning");
800         } else {
801             oa.shouldContain("Warning")
802                     .shouldMatch("The generated CRL.*" + bad + ".*will be disabled");
803         }
804 
805         oa = kt("-printcrl -file " + alias + ".crl");
806         if (bad == null) {
807             oa.shouldNotContain("Warning")
808                     .shouldContain("Verified by " + alias + " in keystore")
809                     .shouldNotContain("(weak");
810         } else {
811             oa.shouldContain("Warning:")
812                     .shouldMatch("The CRL.*" + bad + ".*will be disabled")
813                     .shouldContain("Verified by " + alias + " in keystore")
814                     .shouldContain(bad + " (weak)");
815         }
816     }
817 
checkWeakCertReq( String alias, String options, String bad)818     static void checkWeakCertReq(
819             String alias, String options, String bad) {
820 
821         OutputAnalyzer oa = certreq(alias, options);
822         if (bad == null) {
823             oa.shouldNotContain("Warning");
824         } else {
825             oa.shouldContain("Warning")
826                     .shouldMatch("The generated certificate request.*" + bad + ".*will be disabled");
827         }
828 
829         oa = kt("-printcertreq -file " + alias + ".req");
830         if (bad == null) {
831             oa.shouldNotContain("Warning")
832                     .shouldNotContain("(weak)");
833         } else {
834             oa.shouldContain("Warning")
835                     .shouldMatch("The certificate request.*" + bad + ".*will be disabled")
836                     .shouldContain(bad + " (weak)");
837         }
838     }
839 
840     // This is slow, but real keytool process is launched.
kt1(String cmd, String... input)841     static OutputAnalyzer kt1(String cmd, String... input) {
842         cmd = "-keystore ks -storepass changeit " +
843                 "-keypass changeit " + cmd;
844         System.out.println("---------------------------------------------");
845         try {
846             SecurityTools.setResponse(input);
847             return SecurityTools.keytool(cmd);
848         } catch (Throwable e) {
849             throw new RuntimeException(e);
850         }
851     }
852 
kt(String cmd, String... input)853     static OutputAnalyzer kt(String cmd, String... input) {
854         return kt0("-keystore ks -storepass changeit " +
855                 "-keypass changeit " + cmd, input);
856     }
857 
858     // Fast keytool execution by directly calling its main() method
kt0(String cmd, String... input)859     static OutputAnalyzer kt0(String cmd, String... input) {
860         PrintStream out = System.out;
861         PrintStream err = System.err;
862         InputStream ins = System.in;
863         ByteArrayOutputStream bout = new ByteArrayOutputStream();
864         ByteArrayOutputStream berr = new ByteArrayOutputStream();
865         boolean succeed = true;
866         String sout;
867         String serr;
868         try {
869             System.out.println("---------------------------------------------");
870             System.out.println("$ keytool " + cmd);
871             System.out.println();
872             String feed = "";
873             if (input.length > 0) {
874                 feed = Stream.of(input).collect(Collectors.joining("\n")) + "\n";
875             }
876             System.setIn(new ByteArrayInputStream(feed.getBytes()));
877             System.setOut(new PrintStream(bout));
878             System.setErr(new PrintStream(berr));
879             sun.security.tools.keytool.Main.main(
880                     cmd.trim().split("\\s+"));
881         } catch (Exception e) {
882             // Might be a normal exception when -debug is on or
883             // SecurityException (thrown by jtreg) when System.exit() is called
884             if (!(e instanceof SecurityException)) {
885                 e.printStackTrace();
886             }
887             succeed = false;
888         } finally {
889             System.setOut(out);
890             System.setErr(err);
891             System.setIn(ins);
892             sout = new String(bout.toByteArray());
893             serr = new String(berr.toByteArray());
894             System.out.println("STDOUT:\n" + sout + "\nSTDERR:\n" + serr);
895         }
896         if (!succeed) {
897             throw new RuntimeException();
898         }
899         return new OutputAnalyzer(sout, serr);
900     }
901 
importkeystore(String src, String dest, String options)902     static OutputAnalyzer importkeystore(String src, String dest,
903                                          String options) {
904         return kt0("-importkeystore "
905                 + "-srckeystore " + src + " -destkeystore " + dest
906                 + " -srcstorepass changeit -deststorepass changeit " + options);
907     }
908 
genkeypair(String alias, String options)909     static OutputAnalyzer genkeypair(String alias, String options) {
910         return kt("-genkeypair -alias " + alias + " -dname CN=" + alias
911                 + " -storetype PKCS12 " + options);
912     }
913 
certreq(String alias, String options)914     static OutputAnalyzer certreq(String alias, String options) {
915         return kt("-certreq -alias " + alias
916                 + " -file " + alias + ".req " + options);
917     }
918 
exportcert(String alias)919     static OutputAnalyzer exportcert(String alias) {
920         return kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
921     }
922 
gencert(String relation, String options)923     static OutputAnalyzer gencert(String relation, String options) {
924         int pos = relation.indexOf("-");
925         String issuer = relation.substring(0, pos);
926         String subject = relation.substring(pos + 1);
927         return kt(" -gencert -alias " + issuer + " -infile " + subject
928                 + ".req -outfile " + relation + ".cert " + options);
929     }
930 
saveStore()931     static void saveStore() throws IOException {
932         System.out.println("---------------------------------------------");
933         System.out.println("$ cp ks ks2");
934         Files.copy(Paths.get("ks"), Paths.get("ks2"),
935                 StandardCopyOption.REPLACE_EXISTING);
936     }
937 
reStore()938     static void reStore() throws IOException {
939         System.out.println("---------------------------------------------");
940         System.out.println("$ cp ks2 ks");
941         Files.copy(Paths.get("ks2"), Paths.get("ks"),
942                 StandardCopyOption.REPLACE_EXISTING);
943     }
944 
rm(String s)945     static void rm(String s) throws IOException {
946         System.out.println("---------------------------------------------");
947         System.out.println("$ rm " + s);
948         Files.deleteIfExists(Paths.get(s));
949     }
950 }
951