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 }