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
  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.SecurityTools;
  44 import jdk.test.lib.process.OutputAnalyzer;
  45 import sun.security.tools.KeyStoreUtil;
  46 import sun.security.util.DisabledAlgorithmConstraints;
  47 
  48 import java.io.ByteArrayInputStream;
  49 import java.io.ByteArrayOutputStream;
  50 import java.io.IOException;
  51 import java.io.InputStream;
  52 import java.io.PrintStream;
  53 import java.nio.file.Files;
  54 import java.nio.file.Paths;
  55 import java.nio.file.StandardCopyOption;
  56 import java.security.CryptoPrimitive;
  57 import java.security.KeyStore;
  58 import java.security.cert.X509Certificate;
  59 import java.util.Collections;
  60 import java.util.EnumSet;
  61 import java.util.Set;
  62 import java.util.stream.Collectors;
  63 import java.util.stream.Stream;
  64 
  65 public class WeakAlg {
  66 
  67     public static void main(String[] args) throws Throwable {
  68 
  69         rm("ks");
  70 
  71         // -genkeypair, and -printcert, -list -alias, -exportcert
  72         // (w/ different formats)
  73         checkGenKeyPair("a", "-keyalg RSA -sigalg MD5withRSA", "MD5withRSA");
  74         checkGenKeyPair("b", "-keyalg RSA -keysize 512", "512-bit RSA key");
  75         checkGenKeyPair("c", "-keyalg RSA", null);
  76 
  77         kt("-list")
  78                 .shouldContain("Warning:")
  79                 .shouldMatch("<a>.*MD5withRSA.*risk")
  80                 .shouldMatch("<b>.*512-bit RSA key.*risk");
  81         kt("-list -v")
  82                 .shouldContain("Warning:")
  83                 .shouldMatch("<a>.*MD5withRSA.*risk")
  84                 .shouldContain("MD5withRSA (weak)")
  85                 .shouldMatch("<b>.*512-bit RSA key.*risk")
  86                 .shouldContain("512-bit RSA key (weak)");
  87 
  88         // Multiple warnings for multiple cert in -printcert
  89         // or -list or -exportcert
  90 
  91         // -certreq, -printcertreq, -gencert
  92         checkCertReq("a", "", null);
  93         gencert("c-a", "")
  94                 .shouldNotContain("Warning"); // new sigalg is not weak
  95         gencert("c-a", "-sigalg MD2withRSA")
  96                 .shouldContain("Warning:")
  97                 .shouldMatch("The generated certificate.*MD2withRSA.*risk");
  98 
  99         checkCertReq("a", "-sigalg MD5withRSA", "MD5withRSA");
 100         gencert("c-a", "")
 101                 .shouldContain("Warning:")
 102                 .shouldMatch("The certificate request.*MD5withRSA.*risk");
 103         gencert("c-a", "-sigalg MD2withRSA")
 104                 .shouldContain("Warning:")
 105                 .shouldMatch("The certificate request.*MD5withRSA.*risk")
 106                 .shouldMatch("The generated certificate.*MD2withRSA.*risk");
 107 
 108         checkCertReq("b", "", "512-bit RSA key");
 109         gencert("c-b", "")
 110                 .shouldContain("Warning:")
 111                 .shouldMatch("The certificate request.*512-bit RSA key.*risk")
 112                 .shouldMatch("The generated certificate.*512-bit RSA key.*risk");
 113 
 114         checkCertReq("c", "", null);
 115         gencert("a-c", "")
 116                 .shouldContain("Warning:")
 117                 .shouldMatch("The issuer.*MD5withRSA.*risk");
 118 
 119         // but the new cert is not weak
 120         kt("-printcert -file a-c.cert")
 121                 .shouldNotContain("Warning")
 122                 .shouldNotContain("weak");
 123 
 124         gencert("b-c", "")
 125                 .shouldContain("Warning:")
 126                 .shouldMatch("The issuer.*512-bit RSA key.*risk");
 127 
 128         // -importcert
 129         checkImport();
 130 
 131         // -importkeystore
 132         checkImportKeyStore();
 133 
 134         // -gencrl, -printcrl
 135 
 136         checkGenCRL("a", "", null);
 137         checkGenCRL("a", "-sigalg MD5withRSA", "MD5withRSA");
 138         checkGenCRL("b", "", "512-bit RSA key");
 139         checkGenCRL("c", "", null);
 140 
 141         kt("-delete -alias b");
 142         kt("-printcrl -file b.crl")
 143                 .shouldContain("WARNING: not verified");
 144     }
 145 
 146     static void checkImportKeyStore() throws Exception {
 147 
 148         saveStore();
 149 
 150         rm("ks");
 151         kt("-importkeystore -srckeystore ks2 -srcstorepass changeit")
 152                 .shouldContain("3 entries successfully imported")
 153                 .shouldContain("Warning")
 154                 .shouldMatch("<b>.*512-bit RSA key.*risk")
 155                 .shouldMatch("<a>.*MD5withRSA.*risk");
 156 
 157         rm("ks");
 158         kt("-importkeystore -srckeystore ks2 -srcstorepass changeit -srcalias a")
 159                 .shouldContain("Warning")
 160                 .shouldMatch("<a>.*MD5withRSA.*risk");
 161 
 162         reStore();
 163     }
 164 
 165     static void checkImport() throws Exception {
 166 
 167         saveStore();
 168 
 169         // add trusted cert
 170 
 171         // cert already in
 172         kt("-importcert -alias d -file a.cert", "no")
 173                 .shouldContain("Certificate already exists in keystore")
 174                 .shouldContain("Warning")
 175                 .shouldMatch("The input.*MD5withRSA.*risk")
 176                 .shouldContain("Do you still want to add it?");
 177         kt("-importcert -alias d -file a.cert -noprompt")
 178                 .shouldContain("Warning")
 179                 .shouldMatch("The input.*MD5withRSA.*risk")
 180                 .shouldNotContain("[no]");
 181 
 182         // cert is self-signed
 183         kt("-delete -alias a");
 184         kt("-delete -alias d");
 185         kt("-importcert -alias d -file a.cert", "no")
 186                 .shouldContain("Warning")
 187                 .shouldContain("MD5withRSA (weak)")
 188                 .shouldMatch("The input.*MD5withRSA.*risk")
 189                 .shouldContain("Trust this certificate?");
 190         kt("-importcert -alias d -file a.cert -noprompt")
 191                 .shouldContain("Warning")
 192                 .shouldMatch("The input.*MD5withRSA.*risk")
 193                 .shouldNotContain("[no]");
 194 
 195         // JDK-8177569: no warning for sigalg of trusted cert
 196         String weakSigAlgCA = null;
 197         KeyStore ks = KeyStoreUtil.getCacertsKeyStore();
 198         if (ks != null) {
 199             DisabledAlgorithmConstraints disabledCheck =
 200                     new DisabledAlgorithmConstraints(
 201                             DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
 202             Set<CryptoPrimitive> sigPrimitiveSet = Collections
 203                     .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
 204 
 205             for (String s : Collections.list(ks.aliases())) {
 206                 if (ks.isCertificateEntry(s)) {
 207                     X509Certificate c = (X509Certificate)ks.getCertificate(s);
 208                     String sigAlg = c.getSigAlgName();
 209                     if (!disabledCheck.permits(sigPrimitiveSet, sigAlg, null)) {
 210                         weakSigAlgCA = sigAlg;
 211                         Files.write(Paths.get("ca.cert"),
 212                                 ks.getCertificate(s).getEncoded());
 213                         break;
 214                     }
 215                 }
 216             }
 217         }
 218         if (weakSigAlgCA != null) {
 219             // The following 2 commands still have a warning on why not using
 220             // the -cacerts option directly.
 221             kt("-list -keystore " + KeyStoreUtil.getCacerts())
 222                     .shouldNotContain("risk");
 223             kt("-list -v -keystore " + KeyStoreUtil.getCacerts())
 224                     .shouldNotContain("risk");
 225 
 226             // -printcert will always show warnings
 227             kt("-printcert -file ca.cert")
 228                     .shouldContain("name: " + weakSigAlgCA + " (weak)")
 229                     .shouldContain("Warning")
 230                     .shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
 231             kt("-printcert -file ca.cert -trustcacerts") // -trustcacerts useless
 232                     .shouldContain("name: " + weakSigAlgCA + " (weak)")
 233                     .shouldContain("Warning")
 234                     .shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
 235 
 236             // Importing with -trustcacerts ignore CA cert's sig alg
 237             kt("-delete -alias d");
 238             kt("-importcert -alias d -trustcacerts -file ca.cert", "no")
 239                     .shouldContain("Certificate already exists in system-wide CA")
 240                     .shouldNotContain("risk")
 241                     .shouldContain("Do you still want to add it to your own keystore?");
 242             kt("-importcert -alias d -trustcacerts -file ca.cert -noprompt")
 243                     .shouldNotContain("risk")
 244                     .shouldNotContain("[no]");
 245 
 246             // but not without -trustcacerts
 247             kt("-delete -alias d");
 248             kt("-importcert -alias d -file ca.cert", "no")
 249                     .shouldContain("name: " + weakSigAlgCA + " (weak)")
 250                     .shouldContain("Warning")
 251                     .shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
 252                     .shouldContain("Trust this certificate?");
 253             kt("-importcert -alias d -file ca.cert -noprompt")
 254                     .shouldContain("Warning")
 255                     .shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
 256                     .shouldNotContain("[no]");
 257         }
 258 
 259         // a non self-signed weak cert
 260         reStore();
 261         certreq("b", "");
 262         gencert("c-b", "");
 263         kt("-importcert -alias d -file c-b.cert")   // weak only, no prompt
 264                 .shouldContain("Warning")
 265                 .shouldNotContain("512-bit RSA key (weak)")
 266                 .shouldMatch("The input.*512-bit RSA key.*risk")
 267                 .shouldNotContain("[no]");
 268 
 269         kt("-delete -alias b");
 270         kt("-delete -alias c");
 271         kt("-delete -alias d");
 272 
 273         kt("-importcert -alias d -file c-b.cert", "no") // weak and not trusted
 274                 .shouldContain("Warning")
 275                 .shouldContain("512-bit RSA key (weak)")
 276                 .shouldMatch("The input.*512-bit RSA key.*risk")
 277                 .shouldContain("Trust this certificate?");
 278         kt("-importcert -alias d -file c-b.cert -noprompt")
 279                 .shouldContain("Warning")
 280                 .shouldMatch("The input.*512-bit RSA key.*risk")
 281                 .shouldNotContain("[no]");
 282 
 283         // a non self-signed strong cert
 284         reStore();
 285         certreq("a", "");
 286         gencert("c-a", "");
 287         kt("-importcert -alias d -file c-a.cert") // trusted
 288                 .shouldNotContain("Warning")
 289                 .shouldNotContain("[no]");
 290 
 291         kt("-delete -alias a");
 292         kt("-delete -alias c");
 293         kt("-delete -alias d");
 294 
 295         kt("-importcert -alias d -file c-a.cert", "no") // not trusted
 296                 .shouldNotContain("Warning")
 297                 .shouldContain("Trust this certificate?");
 298         kt("-importcert -alias d -file c-a.cert -noprompt")
 299                 .shouldNotContain("Warning")
 300                 .shouldNotContain("[no]");
 301 
 302         // install reply
 303 
 304         reStore();
 305         certreq("c", "");
 306         gencert("a-c", "");
 307         kt("-importcert -alias c -file a-c.cert")
 308                 .shouldContain("Warning")
 309                 .shouldMatch("Issuer <a>.*MD5withRSA.*risk");
 310 
 311         // JDK-8177569: no warning for sigalg of trusted cert
 312         reStore();
 313         // Change a into a TrustedCertEntry
 314         kt("-exportcert -alias a -file a.cert");
 315         kt("-delete -alias a");
 316         kt("-importcert -alias a -file a.cert -noprompt");
 317         kt("-list -alias a -v")
 318                 .shouldNotContain("weak")
 319                 .shouldNotContain("Warning");
 320         // This time a is trusted and no warning on its weak sig alg
 321         kt("-importcert -alias c -file a-c.cert")
 322                 .shouldNotContain("Warning");
 323 
 324         reStore();
 325 
 326         gencert("a-b", "");
 327         gencert("b-c", "");
 328 
 329         // Full chain with root
 330         cat("a-a-b-c.cert", "b-c.cert", "a-b.cert", "a.cert");
 331         kt("-importcert -alias c -file a-a-b-c.cert")   // only weak
 332                 .shouldContain("Warning")
 333                 .shouldMatch("Reply #2 of 3.*512-bit RSA key.*risk")
 334                 .shouldMatch("Reply #3 of 3.*MD5withRSA.*risk")
 335                 .shouldNotContain("[no]");
 336 
 337         // Without root
 338         cat("a-b-c.cert", "b-c.cert", "a-b.cert");
 339         kt("-importcert -alias c -file a-b-c.cert")     // only weak
 340                 .shouldContain("Warning")
 341                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
 342                 .shouldMatch("Issuer <a>.*MD5withRSA.*risk")
 343                 .shouldNotContain("[no]");
 344 
 345         reStore();
 346         gencert("b-a", "");
 347 
 348         kt("-importcert -alias a -file b-a.cert")
 349                 .shouldContain("Warning")
 350                 .shouldMatch("Issuer <b>.*512-bit RSA key.*risk")
 351                 .shouldNotContain("[no]");
 352 
 353         kt("-importcert -alias a -file c-a.cert")
 354                 .shouldNotContain("Warning");
 355 
 356         kt("-importcert -alias b -file c-b.cert")
 357                 .shouldContain("Warning")
 358                 .shouldMatch("The input.*512-bit RSA key.*risk")
 359                 .shouldNotContain("[no]");
 360 
 361         reStore();
 362         gencert("b-a", "");
 363 
 364         cat("c-b-a.cert", "b-a.cert", "c-b.cert");
 365 
 366         kt("-printcert -file c-b-a.cert")
 367                 .shouldContain("Warning")
 368                 .shouldMatch("The certificate #2 of 2.*512-bit RSA key.*risk");
 369 
 370         kt("-delete -alias b");
 371 
 372         kt("-importcert -alias a -file c-b-a.cert")
 373                 .shouldContain("Warning")
 374                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
 375                 .shouldNotContain("[no]");
 376 
 377         kt("-delete -alias c");
 378         kt("-importcert -alias a -file c-b-a.cert", "no")
 379                 .shouldContain("Top-level certificate in reply:")
 380                 .shouldContain("512-bit RSA key (weak)")
 381                 .shouldContain("Warning")
 382                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
 383                 .shouldContain("Install reply anyway?");
 384         kt("-importcert -alias a -file c-b-a.cert -noprompt")
 385                 .shouldContain("Warning")
 386                 .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
 387                 .shouldNotContain("[no]");
 388 
 389         reStore();
 390     }
 391 
 392     private static void cat(String dest, String... src) throws IOException {
 393         System.out.println("---------------------------------------------");
 394         System.out.printf("$ cat ");
 395 
 396         ByteArrayOutputStream bout = new ByteArrayOutputStream();
 397         for (String s : src) {
 398             System.out.printf(s + " ");
 399             bout.write(Files.readAllBytes(Paths.get(s)));
 400         }
 401         Files.write(Paths.get(dest), bout.toByteArray());
 402         System.out.println("> " + dest);
 403     }
 404 
 405     static void checkGenCRL(String alias, String options, String bad) {
 406 
 407         OutputAnalyzer oa = kt("-gencrl -alias " + alias
 408                 + " -id 1 -file " + alias + ".crl " + options);
 409         if (bad == null) {
 410             oa.shouldNotContain("Warning");
 411         } else {
 412             oa.shouldContain("Warning")
 413                     .shouldMatch("The generated CRL.*" + bad + ".*risk");
 414         }
 415 
 416         oa = kt("-printcrl -file " + alias + ".crl");
 417         if (bad == null) {
 418             oa.shouldNotContain("Warning")
 419                     .shouldContain("Verified by " + alias + " in keystore")
 420                     .shouldNotContain("(weak");
 421         } else {
 422             oa.shouldContain("Warning:")
 423                     .shouldMatch("The CRL.*" + bad + ".*risk")
 424                     .shouldContain("Verified by " + alias + " in keystore")
 425                     .shouldContain(bad + " (weak)");
 426         }
 427     }
 428 
 429     static void checkCertReq(
 430             String alias, String options, String bad) {
 431 
 432         OutputAnalyzer oa = certreq(alias, options);
 433         if (bad == null) {
 434             oa.shouldNotContain("Warning");
 435         } else {
 436             oa.shouldContain("Warning")
 437                     .shouldMatch("The generated certificate request.*" + bad + ".*risk");
 438         }
 439 
 440         oa = kt("-printcertreq -file " + alias + ".req");
 441         if (bad == null) {
 442             oa.shouldNotContain("Warning")
 443                     .shouldNotContain("(weak)");
 444         } else {
 445             oa.shouldContain("Warning")
 446                     .shouldMatch("The certificate request.*" + bad + ".*risk")
 447                     .shouldContain(bad + " (weak)");
 448         }
 449     }
 450 
 451     static void checkGenKeyPair(
 452             String alias, String options, String bad) {
 453 
 454         OutputAnalyzer oa = genkeypair(alias, options);
 455         if (bad == null) {
 456             oa.shouldNotContain("Warning");
 457         } else {
 458             oa.shouldContain("Warning")
 459                     .shouldMatch("The generated certificate.*" + bad + ".*risk");
 460         }
 461 
 462         oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
 463         if (bad == null) {
 464             oa.shouldNotContain("Warning");
 465         } else {
 466             oa.shouldContain("Warning")
 467                     .shouldMatch("The certificate.*" + bad + ".*risk");
 468         }
 469 
 470         oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert");
 471         if (bad == null) {
 472             oa.shouldNotContain("Warning");
 473         } else {
 474             oa.shouldContain("Warning")
 475                     .shouldMatch("The certificate.*" + bad + ".*risk");
 476         }
 477 
 478         oa = kt("-printcert -rfc -file " + alias + ".cert");
 479         if (bad == null) {
 480             oa.shouldNotContain("Warning");
 481         } else {
 482             oa.shouldContain("Warning")
 483                     .shouldMatch("The certificate.*" + bad + ".*risk");
 484         }
 485 
 486         oa = kt("-list -alias " + alias);
 487         if (bad == null) {
 488             oa.shouldNotContain("Warning");
 489         } else {
 490             oa.shouldContain("Warning")
 491                     .shouldMatch("The certificate.*" + bad + ".*risk");
 492         }
 493 
 494         // With cert content
 495 
 496         oa = kt("-printcert -file " + alias + ".cert");
 497         if (bad == null) {
 498             oa.shouldNotContain("Warning");
 499         } else {
 500             oa.shouldContain("Warning")
 501                     .shouldContain(bad + " (weak)")
 502                     .shouldMatch("The certificate.*" + bad + ".*risk");
 503         }
 504 
 505         oa = kt("-list -v -alias " + alias);
 506         if (bad == null) {
 507             oa.shouldNotContain("Warning");
 508         } else {
 509             oa.shouldContain("Warning")
 510                     .shouldContain(bad + " (weak)")
 511                     .shouldMatch("The certificate.*" + bad + ".*risk");
 512         }
 513     }
 514 
 515     // This is slow, but real keytool process is launched.
 516     static OutputAnalyzer kt1(String cmd, String... input) {
 517         cmd = "-keystore ks -storepass changeit " +
 518                 "-keypass changeit " + cmd;
 519         System.out.println("---------------------------------------------");
 520         try {
 521             SecurityTools.setResponse(input);
 522             return SecurityTools.keytool(cmd);
 523         } catch (Throwable e) {
 524             throw new RuntimeException(e);
 525         }
 526     }
 527 
 528     // Fast keytool execution by directly calling its main() method
 529     static OutputAnalyzer kt(String cmd, String... input) {
 530         PrintStream out = System.out;
 531         PrintStream err = System.err;
 532         InputStream ins = System.in;
 533         ByteArrayOutputStream bout = new ByteArrayOutputStream();
 534         ByteArrayOutputStream berr = new ByteArrayOutputStream();
 535         boolean succeed = true;
 536         try {
 537             cmd = "-keystore ks -storepass changeit " +
 538                     "-keypass changeit " + cmd;
 539             System.out.println("---------------------------------------------");
 540             System.out.println("$ keytool " + cmd);
 541             System.out.println();
 542             String feed = "";
 543             if (input.length > 0) {
 544                 feed = Stream.of(input).collect(Collectors.joining("\n")) + "\n";
 545             }
 546             System.setIn(new ByteArrayInputStream(feed.getBytes()));
 547             System.setOut(new PrintStream(bout));
 548             System.setErr(new PrintStream(berr));
 549             sun.security.tools.keytool.Main.main(
 550                     cmd.trim().split("\\s+"));
 551         } catch (Exception e) {
 552             // Might be a normal exception when -debug is on or
 553             // SecurityException (thrown by jtreg) when System.exit() is called
 554             if (!(e instanceof SecurityException)) {
 555                 e.printStackTrace();
 556             }
 557             succeed = false;
 558         } finally {
 559             System.setOut(out);
 560             System.setErr(err);
 561             System.setIn(ins);
 562         }
 563         String sout = new String(bout.toByteArray());
 564         String serr = new String(berr.toByteArray());
 565         System.out.println("STDOUT:\n" + sout + "\nSTDERR:\n" + serr);
 566         if (!succeed) {
 567             throw new RuntimeException();
 568         }
 569         return new OutputAnalyzer(sout, serr);
 570     }
 571 
 572     static OutputAnalyzer genkeypair(String alias, String options) {
 573         return kt("-genkeypair -alias " + alias + " -dname CN=" + alias
 574                 + " -keyalg RSA -storetype JKS " + options);
 575     }
 576 
 577     static OutputAnalyzer certreq(String alias, String options) {
 578         return kt("-certreq -alias " + alias
 579                 + " -file " + alias + ".req " + options);
 580     }
 581 
 582     static OutputAnalyzer exportcert(String alias) {
 583         return kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
 584     }
 585 
 586     static OutputAnalyzer gencert(String relation, String options) {
 587         int pos = relation.indexOf("-");
 588         String issuer = relation.substring(0, pos);
 589         String subject = relation.substring(pos + 1);
 590         return kt(" -gencert -alias " + issuer + " -infile " + subject
 591                 + ".req -outfile " + relation + ".cert " + options);
 592     }
 593 
 594     static void saveStore() throws IOException {
 595         System.out.println("---------------------------------------------");
 596         System.out.println("$ cp ks ks2");
 597         Files.copy(Paths.get("ks"), Paths.get("ks2"),
 598                 StandardCopyOption.REPLACE_EXISTING);
 599     }
 600 
 601     static void reStore() throws IOException {
 602         System.out.println("---------------------------------------------");
 603         System.out.println("$ cp ks2 ks");
 604         Files.copy(Paths.get("ks2"), Paths.get("ks"),
 605                 StandardCopyOption.REPLACE_EXISTING);
 606     }
 607 
 608     static void rm(String s) throws IOException {
 609         System.out.println("---------------------------------------------");
 610         System.out.println("$ rm " + s);
 611         Files.deleteIfExists(Paths.get(s));
 612     }
 613 }