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