• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ebourg / jsign / #419

23 Jul 2026 05:42PM UTC coverage: 81.36% (+0.08%) from 81.28%
#419

push

ebourg
Use tabs for the Gradle syntax snippets

5526 of 6792 relevant lines covered (81.36%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.05
/jsign-crypto/src/main/java/net/jsign/KeyStoreBuilder.java
1
/*
2
 * Copyright 2023 Emmanuel Bourg
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package net.jsign;
18

19
import java.io.File;
20
import java.io.IOException;
21
import java.nio.charset.StandardCharsets;
22
import java.nio.file.Files;
23
import java.nio.file.Path;
24
import java.security.KeyStore;
25
import java.security.KeyStoreException;
26
import java.security.Provider;
27
import java.util.stream.Collectors;
28
import java.util.stream.Stream;
29

30
import static net.jsign.KeyStoreType.*;
31

32
/**
33
 * Keystore builder.
34
 *
35
 * <p>Example:</p>
36
 *
37
 * <pre>
38
 *   KeyStore keystore = new KeyStoreBuilder().storetype(PKCS12).keystore("keystore.p12").storepass("password").build();
39
 * </pre>
40
 *
41
 * @since 5.0
42
 */
43
public class KeyStoreBuilder {
44

45
    /** The name used to refer to a configuration parameter */
46
    private String parameterName = "parameter";
1✔
47

48
    private String keystore;
49
    private String storepass;
50
    private KeyStoreType storetype;
51
    private String keypass;
52
    private File keyfile;
53
    private File certfile;
54

55
    /** The base directory to resolve the relative paths */
56
    private File basedir = new File("empty").getParentFile();
1✔
57

58
    private Provider provider;
59

60
    public KeyStoreBuilder() {
1✔
61
    }
1✔
62

63
    KeyStoreBuilder(String parameterName) {
1✔
64
        this.parameterName = parameterName;
1✔
65
    }
1✔
66

67
    String parameterName() {
68
        return parameterName;
1✔
69
    }
70

71
    /**
72
     * Sets the file containing the keystore.
73
     */
74
    public KeyStoreBuilder keystore(File keystore) {
75
        return keystore(keystore.getPath());
1✔
76
    }
77

78
    /**
79
     * Sets the name of the resource containing the keystore. Either the path of the keystore file,
80
     * the SunPKCS11 configuration file or the cloud keystore name depending on the type of keystore.
81
     */
82
    public KeyStoreBuilder keystore(String keystore) {
83
        this.keystore = keystore;
1✔
84
        return this;
1✔
85
    }
86

87
    String keystore() {
88
        return keystore;
1✔
89
    }
90

91
    /**
92
     * Sets the password to access the keystore. The password can be loaded from a file by using the <code>file:</code>
93
     * prefix followed by the path of the file, or from an environment variable by using the <code>env:</code> prefix
94
     * followed by the name of the variable.
95
     */
96
    public KeyStoreBuilder storepass(String storepass) {
97
        this.storepass = storepass;
1✔
98
        return this;
1✔
99
    }
100

101
    String storepass() {
102
        storepass = readPassword("storepass", storepass);
1✔
103
        return storepass;
1✔
104
    }
105

106
    /**
107
     * Sets the type of the keystore.
108
     */
109
    public KeyStoreBuilder storetype(KeyStoreType storetype) {
110
        this.storetype = storetype;
1✔
111
        return this;
1✔
112
    }
113

114
    /**
115
     * Sets the type of the keystore.
116
     *
117
     * @param storetype the type of the keystore
118
     * @throws IllegalArgumentException if the type is not recognized
119
     */
120
    public KeyStoreBuilder storetype(String storetype) {
121
        try {
122
            this.storetype = storetype != null ? KeyStoreType.valueOf(storetype.toUpperCase()) : null;
1✔
123
        } catch (IllegalArgumentException e) {
×
124
            String expectedTypes = Stream.of(KeyStoreType.values())
×
125
                    .filter(type -> type != NONE).map(KeyStoreType::name)
×
126
                    .collect(Collectors.joining(", "));
×
127
            throw new IllegalArgumentException("Unknown keystore type '" + storetype + "' (expected types: " + expectedTypes + ")");
×
128
        }
1✔
129
        return this;
1✔
130
    }
131

132
    KeyStoreType storetype() {
133
        if (storetype == null) {
1✔
134
            if (keystore == null) {
1✔
135
                // no keystore specified, keyfile and certfile are expected
136
                storetype = NONE;
1✔
137
            } else {
138
                // the keystore type wasn't specified, let's try to guess it
139
                File file = createFile(keystore);
1✔
140
                if (!file.isFile()) {
1✔
141
                    throw new IllegalArgumentException("Keystore file '" + keystore + "' not found");
1✔
142
                }
143
                storetype = KeyStoreType.of(file);
1✔
144
                if (storetype == null) {
1✔
145
                    throw new IllegalArgumentException("Keystore type of '" + keystore + "' not recognized");
1✔
146
                }
147
            }
148
        }
149
        return storetype;
1✔
150
    }
151

152
    /**
153
     * Sets the password to access the private key. The password can be loaded from a file by using the <code>file:</code>
154
     * prefix followed by the path of the file, or from an environment variable by using the <code>env:</code> prefix
155
     * followed by the name of the variable.
156
     */
157
    public KeyStoreBuilder keypass(String keypass) {
158
        this.keypass = keypass;
1✔
159
        return this;
1✔
160
    }
161

162
    String keypass() {
163
        keypass = readPassword("keypass", keypass);
1✔
164
        return keypass;
1✔
165
    }
166

167
    /**
168
     * Sets the file containing the private key.
169
     */
170
    public KeyStoreBuilder keyfile(String keyfile) {
171
        return keyfile(createFile(keyfile));
1✔
172
    }
173

174
    /**
175
     * Sets the file containing the private key.
176
     */
177
    public KeyStoreBuilder keyfile(File keyfile) {
178
        this.keyfile = keyfile;
1✔
179
        return this;
1✔
180
    }
181

182
    File keyfile() {
183
        return keyfile;
1✔
184
    }
185

186
    /**
187
     * Sets the file containing the certificate chain.
188
     */
189
    public KeyStoreBuilder certfile(String certfile) {
190
        return certfile(createFile(certfile));
1✔
191
    }
192

193
    /**
194
     * Sets the file containing the certificate chain.
195
     */
196
    public KeyStoreBuilder certfile(File certfile) {
197
        this.certfile = certfile;
1✔
198
        return this;
1✔
199
    }
200

201
    File certfile() {
202
        return certfile;
1✔
203
    }
204

205
    void setBaseDir(File basedir) {
206
        this.basedir = basedir;
1✔
207
    }
1✔
208

209
    File createFile(String file) {
210
        if (file == null) {
1✔
211
            return null;
1✔
212
        }
213

214
        if (new File(file).isAbsolute()) {
1✔
215
            return new File(file);
1✔
216
        } else {
217
            return new File(basedir, file);
1✔
218
        }
219
    }
220

221
    /**
222
     * Read the password from the specified value. If the value is prefixed with <code>file:</code>
223
     * the password is loaded from a file. If the value is prefixed with <code>env:</code> the password
224
     * is loaded from an environment variable. Otherwise the value is returned as is.
225
     *
226
     * @param name  the name of the parameter
227
     * @param value the value to parse
228
     */
229
    private String readPassword(String name, String value) {
230
        if (value != null) {
1✔
231
            if (value.startsWith("file:")) {
1✔
232
                String filename = value.substring("file:".length());
1✔
233
                Path path = createFile(filename).toPath();
1✔
234
                try {
235
                    value = String.join("\n", Files.readAllLines(path, StandardCharsets.UTF_8)).trim();
1✔
236
                } catch (IOException e) {
1✔
237
                    throw new IllegalArgumentException("Failed to read the " + name + " " + parameterName + " from the file '" + filename + "'", e);
1✔
238
                }
1✔
239
            } else if (value.startsWith("env:")) {
1✔
240
                String variable = value.substring("env:".length());
1✔
241
                if (!System.getenv().containsKey(variable)) {
1✔
242
                    throw new IllegalArgumentException("Failed to read the " + name + " " + parameterName + ", the '" + variable + "' environment variable is not defined");
1✔
243
                }
244
                value = System.getenv(variable);
1✔
245
            }
246
        }
247

248
        return value;
1✔
249
    }
250

251
    /**
252
     * Validates the parameters.
253
     */
254
    void validate() throws IllegalArgumentException {
255
        // keystore or keyfile, but not both
256
        if (keystore != null && keyfile != null) {
1✔
257
            throw new IllegalArgumentException("keystore " + parameterName + " can't be mixed with keyfile");
1✔
258
        }
259

260
        if (keystore == null && keyfile == null && certfile == null && storetype == null) {
1✔
261
            throw new IllegalArgumentException("Either keystore, or keyfile and certfile, or storetype " + parameterName + "s must be set");
1✔
262
        }
263

264
        storetype().validate(this);
1✔
265
    }
1✔
266

267
    /**
268
     * Returns the provider used to sign with the keystore.
269
     */
270
    public Provider provider() {
271
        if (provider == null) {
1✔
272
            provider = storetype().getProvider(this);
1✔
273
        }
274
        return provider;
1✔
275
    }
276

277
    /**
278
     * Builds the keystore.
279
     *
280
     * @throws IllegalArgumentException if the parameters are invalid
281
     * @throws KeyStoreException if the keystore can't be loaded
282
     */
283
    public KeyStore build() throws KeyStoreException {
284
        validate();
1✔
285
        return storetype().getKeystore(this, provider());
1✔
286
    }
287

288
    /**
289
     * Returns a java.security.KeyStore.Builder using the parameters of this builder.
290
     */
291
    public KeyStore.Builder builder() {
292
        return new KeyStore.Builder() {
1✔
293
            @Override
294
            public KeyStore getKeyStore() throws KeyStoreException {
295
                return build();
1✔
296
            }
297

298
            @Override
299
            public KeyStore.ProtectionParameter getProtectionParameter(String alias) {
300
                String storepass = storepass();
1✔
301
                return new KeyStore.PasswordProtection(storepass != null ? storepass.toCharArray() : null);
1✔
302
            }
303
        };
304
    }
305
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc