View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2012-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.jcabi.ssl.maven.plugin;
6   
7   import com.jcabi.aspects.Immutable;
8   import com.jcabi.aspects.Loggable;
9   import com.jcabi.log.Logger;
10  import com.jcabi.log.VerboseProcess;
11  import java.io.File;
12  import java.io.IOException;
13  import java.util.ArrayList;
14  import java.util.List;
15  import java.util.Properties;
16  import java.util.logging.Level;
17  import javax.validation.constraints.NotNull;
18  import lombok.EqualsAndHashCode;
19  import lombok.ToString;
20  import org.apache.commons.io.FileUtils;
21  
22  /**
23   * Abstraction of {@code java.home/lib/security/cacerts} file.
24   *
25   * @since 0.5
26   */
27  @Immutable
28  @ToString
29  @EqualsAndHashCode(of = "store")
30  final class Cacerts {
31  
32      /**
33       * Constant {@code javax.net.ssl.trustStore}.
34       */
35      public static final String TRUST = "javax.net.ssl.trustStore";
36  
37      /**
38       * Constant {@code javax.net.ssl.trustStorePassword}.
39       */
40      public static final String TRUST_PWD = "javax.net.ssl.trustStorePassword";
41  
42      /**
43       * Standard password of {@code cacerts} file.
44       */
45      public static final String STD_PWD = "changeit";
46  
47      /**
48       * New location of the trust store.
49       */
50      private final transient String store;
51  
52      /**
53       * Ctor.
54       * @param file New location
55       * @throws IOException If fails
56       */
57      @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors")
58      Cacerts(@NotNull final File file) throws IOException {
59          this.store = file.getAbsolutePath();
60          final File prev = new File(
61              String.format(
62                  "%s/lib/security/cacerts",
63                  System.getProperty("java.home")
64              )
65          ).toPath().toRealPath().toFile();
66          Cacerts.convert(prev, file);
67          file.setWritable(true);
68          Logger.info(
69              this,
70              "Existing cacerts '%s' imported to '%s' (%s)",
71              prev,
72              this.store,
73              FileUtils.byteCountToDisplaySize(file.length())
74          );
75      }
76  
77      /**
78       * Import existing keystore content into this trust store.
79       * @throws IOException If fails
80       */
81      public void imprt() throws IOException {
82          final File keystore = new File(System.getProperty(Keystore.KEY));
83          new Keytool(new File(this.store), Cacerts.STD_PWD).imprt(
84              keystore, System.getProperty(Keystore.KEY_PWD)
85          );
86          System.setProperty(Cacerts.TRUST, this.store);
87          System.setProperty(Cacerts.TRUST_PWD, Cacerts.STD_PWD);
88          Logger.info(
89              this,
90              "keyStore '%s' imported into trustStore '%s'",
91              keystore,
92              this.store
93          );
94      }
95  
96      /**
97       * Populate given properties with this truststore's path and password.
98       *
99       * @param props The properties
100      */
101     @Loggable(Loggable.DEBUG)
102     public void populate(final Properties props) {
103         final String[] names = {Cacerts.TRUST, Cacerts.TRUST_PWD};
104         for (final String name : names) {
105             final String value = System.getProperty(name);
106             if (value == null) {
107                 continue;
108             }
109             props.put(name, value);
110             Logger.info(
111                 this,
112                 "Maven property ${%s} set to '%s'",
113                 name,
114                 value
115             );
116         }
117     }
118 
119     /**
120      * Convert cacerts from any format to JKS.
121      * @param src Source cacerts file
122      * @param dest Destination JKS file
123      * @throws IOException If fails
124      */
125     private static void convert(final File src, final File dest)
126         throws IOException {
127         dest.getParentFile().mkdirs();
128         final List<String> cmds = new ArrayList<>(15);
129         cmds.add(
130             String.format(
131                 "%s/bin/keytool",
132                 System.getProperty("java.home")
133             )
134         );
135         cmds.add("-importkeystore");
136         cmds.add("-srckeystore");
137         cmds.add(src.getAbsolutePath());
138         cmds.add("-srcstorepass");
139         cmds.add(Cacerts.STD_PWD);
140         cmds.add("-destkeystore");
141         cmds.add(dest.getAbsolutePath());
142         cmds.add("-deststorepass");
143         cmds.add(Cacerts.STD_PWD);
144         cmds.add("-deststoretype");
145         cmds.add("jks");
146         cmds.add("-noprompt");
147         final ProcessBuilder builder = new ProcessBuilder(cmds);
148         builder.environment().put(
149             "JAVA_TOOL_OPTIONS",
150             "-Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8"
151         );
152         new VerboseProcess(builder, Level.FINE, Level.FINE).stdout();
153     }
154 }