View Javadoc
1   /**
2    * Copyright (c) 2012-2022, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.ssl.maven.plugin;
31  
32  import com.jcabi.aspects.Immutable;
33  import com.jcabi.aspects.Loggable;
34  import com.jcabi.log.Logger;
35  import com.jcabi.log.VerboseProcess;
36  import java.io.File;
37  import java.io.IOException;
38  import java.io.OutputStreamWriter;
39  import java.io.PrintWriter;
40  import java.util.ArrayList;
41  import java.util.List;
42  import java.util.Locale;
43  import lombok.EqualsAndHashCode;
44  import lombok.ToString;
45  import org.apache.commons.io.FileUtils;
46  
47  /**
48   * Keytool abstraction.
49   *
50   * @author Yegor Bugayenko (yegor256@gmail.com)
51   * @version $Id: fcfa8f584ec19a88d45eb7e000ae0b44cdd5d9a9 $
52   * @since 0.5
53   */
54  @Immutable
55  @ToString
56  @EqualsAndHashCode(of = { "keystore", "password" })
57  final class Keytool {
58      /**
59       * Localhost, input to the keytool.
60       */
61      private static final String LOCALHOST = "localhost";
62  
63      /**
64       * Platform-dependent line separator.
65       */
66      private static final String NEWLINE = System.getProperty("line.separator");
67  
68      /**
69       * Keystore location.
70       */
71      private final transient String keystore;
72  
73      /**
74       * Keystore password.
75       */
76      private final transient String password;
77  
78      /**
79       * Public ctor.
80       * @param store The location of keystore
81       * @param pwd The password
82       */
83      public Keytool(final File store, final String pwd) {
84          this.keystore = store.getAbsolutePath();
85          this.password = pwd;
86      }
87  
88      /**
89       * List content of the keystore.
90       * @return The content of it
91       * @throws IOException If fails
92       */
93      @Loggable(Loggable.DEBUG)
94      public String list() throws IOException {
95          return new VerboseProcess(this.proc("-list", "-v")).stdout();
96      }
97  
98      /**
99       * Generate key.
100      * @throws IOException If fails
101      */
102     @Loggable(Loggable.DEBUG)
103     public void genkey() throws IOException {
104         final Process proc = this.proc(
105             "-genkeypair",
106             "-alias",
107             Keytool.LOCALHOST,
108             "-keyalg",
109             "RSA",
110             "-keysize",
111             "2048",
112             "-keypass",
113             this.password
114         ).start();
115         final PrintWriter writer = new PrintWriter(
116             new OutputStreamWriter(proc.getOutputStream())
117         );
118         writer.print(this.appendNewLine(Keytool.LOCALHOST));
119         writer.print(this.appendNewLine("ACME Co."));
120         writer.print(this.appendNewLine("software developers"));
121         writer.print(this.appendNewLine("San Francisco"));
122         writer.print(this.appendNewLine("California"));
123         writer.print(this.appendNewLine("US"));
124         writer.print(this.appendNewLine(this.createLocaleDependentYes()));
125         writer.close();
126         new VerboseProcess(proc).stdout();
127         Logger.info(
128             this,
129             "Keystore created in '%s' (%s)",
130             this.keystore,
131             FileUtils.byteCountToDisplaySize(this.keystore.length())
132         );
133     }
134 
135     /**
136      * Import certificate into this store.
137      * @param file The file to import
138      * @param pwd The password there
139      * @throws IOException If fails
140      */
141     @Loggable(Loggable.DEBUG)
142     public void imprt(final File file, final String pwd) throws IOException {
143         new VerboseProcess(
144             this.proc(
145                 "-importkeystore",
146                 "-srckeystore",
147                 file.getAbsolutePath(),
148                 "-srcstorepass",
149                 pwd,
150                 "-destkeystore",
151                 this.keystore,
152                 "-deststorepass",
153                 this.password
154             )
155         ).stdout();
156     }
157 
158     /**
159      * Creates a string, which consists of string with an appended
160      * platform-dependent line separator.
161      * @param text Text, to which the line separator needs to be appended
162      * @return Contents of text with appended line separator
163      */
164     private String appendNewLine(final String text) {
165         return String.format("%s%s", text, NEWLINE);
166     }
167 
168     /**
169      * Creates a text, which represents "yes" in the language,
170      * specified by the current locale.
171      * @return The word "Yes" translated to the current language
172      */
173     private String createLocaleDependentYes() {
174         return new Yes().translate(Locale.getDefault());
175     }
176 
177     /**
178      * Create process builder.
179      * @param args Arguments
180      * @return Process just created and started
181      * @throws IOException If fails
182      */
183     private ProcessBuilder proc(final String... args) throws IOException {
184         final List<String> cmds = new ArrayList<String>(args.length + 1);
185         cmds.add(
186             String.format(
187                 "%s/bin/keytool",
188                 System.getProperty("java.home")
189             )
190         );
191         for (final String arg : args) {
192             cmds.add(arg);
193         }
194         cmds.add("-storetype");
195         cmds.add("jks");
196         cmds.add("-noprompt");
197         cmds.add("-storepass");
198         cmds.add(this.password);
199         cmds.add("-keystore");
200         cmds.add(this.keystore);
201         return new ProcessBuilder(cmds);
202     }
203 }