0
# SSL Utilities
1
2
Comprehensive utility functions for keystore operations, PEM file parsing, certificate handling, SSL-related tasks, and low-level ASN.1/DER parsing with extensive validation and error handling.
3
4
## Capabilities
5
6
### KeyStore Utilities
7
8
Comprehensive utilities for keystore manipulation, type detection, and key/trust manager creation.
9
10
```java { .api }
11
/**
12
* Utility methods for working with KeyStore instances
13
*/
14
public final class KeyStoreUtil {
15
/**
16
* Infers keystore type from file path extension
17
* @param path keystore file path
18
* @return inferred keystore type (JKS, PKCS12, etc.)
19
*/
20
public static String inferKeyStoreType(String path);
21
22
/**
23
* Reads a keystore from file with specified type and password
24
* @param path path to keystore file
25
* @param ksType keystore type (JKS, PKCS12, etc.)
26
* @param password keystore password
27
* @return loaded KeyStore instance
28
* @throws GeneralSecurityException if keystore loading fails
29
* @throws IOException if file reading fails
30
*/
31
public static KeyStore readKeyStore(Path path, String ksType, char[] password)
32
throws GeneralSecurityException, IOException;
33
34
/**
35
* Builds a keystore from certificate chain and private key
36
* @param certificateChain collection of certificates (first should be end entity)
37
* @param privateKey private key corresponding to end entity certificate
38
* @param password password for keystore and private key protection
39
* @return KeyStore containing the certificate chain and private key
40
* @throws GeneralSecurityException if keystore creation fails
41
*/
42
public static KeyStore buildKeyStore(Collection<Certificate> certificateChain,
43
PrivateKey privateKey, char[] password)
44
throws GeneralSecurityException;
45
46
/**
47
* Filters keystore entries based on predicate
48
* @param store original keystore
49
* @param filter predicate to test keystore entries
50
* @return new KeyStore containing only entries that match the filter
51
*/
52
public static KeyStore filter(KeyStore store, Predicate<KeyStoreEntry> filter);
53
54
/**
55
* Builds a truststore from collection of certificates
56
* @param certificates trusted certificates to include
57
* @return KeyStore configured as truststore
58
* @throws GeneralSecurityException if truststore creation fails
59
*/
60
public static KeyStore buildTrustStore(Iterable<Certificate> certificates)
61
throws GeneralSecurityException;
62
63
/**
64
* Builds a truststore with specific type from certificates
65
* @param certificates trusted certificates to include
66
* @param type keystore type for truststore
67
* @return KeyStore of specified type configured as truststore
68
* @throws GeneralSecurityException if truststore creation fails
69
*/
70
public static KeyStore buildTrustStore(Iterable<Certificate> certificates, String type)
71
throws GeneralSecurityException;
72
73
/**
74
* Creates key manager from certificate chain and private key
75
* @param certificateChain certificate chain (end entity first)
76
* @param privateKey private key for end entity certificate
77
* @param password password for key protection
78
* @return configured X509ExtendedKeyManager
79
* @throws GeneralSecurityException if key manager creation fails
80
* @throws IOException if certificate/key processing fails
81
*/
82
public static X509ExtendedKeyManager createKeyManager(Certificate[] certificateChain,
83
PrivateKey privateKey, char[] password)
84
throws GeneralSecurityException, IOException;
85
86
/**
87
* Creates key manager from keystore
88
* @param keyStore keystore containing private keys and certificates
89
* @param password password for private key access
90
* @param algorithm key manager algorithm (e.g., "SunX509", "PKIX")
91
* @return configured X509ExtendedKeyManager
92
* @throws GeneralSecurityException if key manager creation fails
93
*/
94
public static X509ExtendedKeyManager createKeyManager(KeyStore keyStore, char[] password,
95
String algorithm)
96
throws GeneralSecurityException;
97
98
/**
99
* Creates trust manager from truststore
100
* @param trustStore truststore containing trusted certificates (null for JDK defaults)
101
* @param algorithm trust manager algorithm (e.g., "PKIX", "SunX509")
102
* @return configured X509ExtendedTrustManager
103
* @throws NoSuchAlgorithmException if algorithm is not available
104
* @throws KeyStoreException if truststore access fails
105
*/
106
public static X509ExtendedTrustManager createTrustManager(@Nullable KeyStore trustStore,
107
String algorithm)
108
throws NoSuchAlgorithmException, KeyStoreException;
109
110
/**
111
* Creates trust manager from collection of certificates
112
* @param certificates trusted certificates
113
* @return configured X509ExtendedTrustManager
114
* @throws GeneralSecurityException if trust manager creation fails
115
*/
116
public static X509ExtendedTrustManager createTrustManager(Collection<Certificate> certificates)
117
throws GeneralSecurityException;
118
119
/**
120
* Creates stream of keystore entries for processing
121
* @param keyStore keystore to stream
122
* @param exceptionHandler function to handle GeneralSecurityException during streaming
123
* @return Stream of KeyStoreEntry objects
124
*/
125
public static Stream<KeyStoreEntry> stream(KeyStore keyStore,
126
Function<GeneralSecurityException, ? extends RuntimeException> exceptionHandler);
127
}
128
```
129
130
**Usage Examples:**
131
132
```java
133
import org.elasticsearch.common.ssl.KeyStoreUtil;
134
import java.security.KeyStore;
135
import java.security.PrivateKey;
136
import java.security.cert.Certificate;
137
import java.nio.file.Paths;
138
import java.util.Collection;
139
import java.util.List;
140
141
// Infer keystore type
142
String type = KeyStoreUtil.inferKeyStoreType("keystore.p12"); // returns "PKCS12"
143
String jksType = KeyStoreUtil.inferKeyStoreType("keystore.jks"); // returns "JKS"
144
145
// Read keystore from file
146
KeyStore keystore = KeyStoreUtil.readKeyStore(
147
Paths.get("/etc/ssl/keystore.p12"),
148
"PKCS12",
149
"password".toCharArray()
150
);
151
152
// Build keystore from certificates and key
153
Collection<Certificate> certChain = List.of(endEntityCert, intermediateCert);
154
KeyStore builtKeystore = KeyStoreUtil.buildKeyStore(
155
certChain,
156
privateKey,
157
"password".toCharArray()
158
);
159
160
// Filter keystore entries
161
KeyStore filteredKeystore = KeyStoreUtil.filter(keystore, entry ->
162
entry.getAlias().startsWith("server-"));
163
164
// Build truststore from CA certificates
165
KeyStore truststore = KeyStoreUtil.buildTrustStore(caCertificates);
166
167
// Create key manager
168
X509ExtendedKeyManager keyManager = KeyStoreUtil.createKeyManager(
169
certChain.toArray(new Certificate[0]),
170
privateKey,
171
"password".toCharArray()
172
);
173
174
// Create trust manager from certificates
175
X509ExtendedTrustManager trustManager = KeyStoreUtil.createTrustManager(caCertificates);
176
177
// Stream keystore entries
178
KeyStoreUtil.stream(keystore, ex -> new RuntimeException(ex))
179
.filter(entry -> entry.isKeyEntry())
180
.forEach(entry -> {
181
System.out.println("Key alias: " + entry.getAlias());
182
X509Certificate cert = entry.getX509Certificate();
183
System.out.println("Subject: " + cert.getSubjectX500Principal());
184
});
185
```
186
187
### KeyStore Entry Wrapper
188
189
Wrapper class for individual keystore entries providing convenient access methods.
190
191
```java { .api }
192
/**
193
* Wrapper for individual keystore entries
194
*/
195
public static class KeyStoreEntry {
196
/**
197
* Gets the alias of this keystore entry
198
* @return entry alias
199
*/
200
public String getAlias();
201
202
/**
203
* Gets the certificate as X509Certificate
204
* @return X509Certificate or null if not a certificate entry
205
*/
206
public X509Certificate getX509Certificate();
207
208
/**
209
* Checks if this entry contains a private key
210
* @return true if this is a key entry
211
*/
212
public boolean isKeyEntry();
213
214
/**
215
* Gets the private key from this entry
216
* @param password password for private key access
217
* @return PrivateKey or null if not a key entry or wrong password
218
*/
219
public PrivateKey getKey(char[] password);
220
221
/**
222
* Gets the certificate chain for this entry
223
* @return list of certificates in the chain
224
*/
225
public List<? extends X509Certificate> getX509CertificateChain();
226
227
/**
228
* Deletes this entry from the keystore
229
*/
230
public void delete();
231
}
232
```
233
234
### PEM Utilities
235
236
Utilities for parsing PEM formatted certificates and private keys with support for encrypted keys.
237
238
```java { .api }
239
/**
240
* Utilities for parsing PEM formatted certificates and private keys
241
*/
242
public final class PemUtils {
243
/**
244
* Reads private key from PEM file with optional password
245
* @param path path to PEM private key file
246
* @param passwordSupplier supplier for key password (called only if key is encrypted)
247
* @return parsed PrivateKey
248
* @throws IOException if file reading fails
249
* @throws GeneralSecurityException if key parsing fails
250
*/
251
public static PrivateKey readPrivateKey(Path path, Supplier<char[]> passwordSupplier)
252
throws IOException, GeneralSecurityException;
253
254
/**
255
* Parses PKCS#8 PEM formatted private key string
256
* @param pemString PEM formatted private key string
257
* @return parsed PrivateKey
258
* @throws IOException if PEM parsing fails
259
* @throws GeneralSecurityException if key parsing fails
260
*/
261
public static PrivateKey parsePKCS8PemString(String pemString)
262
throws IOException, GeneralSecurityException;
263
264
/**
265
* Reads certificates from multiple PEM files
266
* @param certPaths collection of paths to PEM certificate files
267
* @return list of parsed certificates
268
* @throws CertificateException if certificate parsing fails
269
* @throws IOException if file reading fails
270
*/
271
public static List<Certificate> readCertificates(Collection<Path> certPaths)
272
throws CertificateException, IOException;
273
}
274
```
275
276
**Usage Examples:**
277
278
```java
279
import org.elasticsearch.common.ssl.PemUtils;
280
import java.security.PrivateKey;
281
import java.security.cert.Certificate;
282
import java.nio.file.Paths;
283
import java.util.List;
284
import java.util.function.Supplier;
285
286
// Read unencrypted private key
287
PrivateKey privateKey = PemUtils.readPrivateKey(
288
Paths.get("/etc/ssl/private/server.key"),
289
() -> null // no password needed
290
);
291
292
// Read encrypted private key
293
PrivateKey encryptedKey = PemUtils.readPrivateKey(
294
Paths.get("/etc/ssl/private/encrypted.key"),
295
() -> "secret123".toCharArray() // provide password when needed
296
);
297
298
// Read encrypted key with interactive password prompt
299
PrivateKey interactiveKey = PemUtils.readPrivateKey(
300
Paths.get("/etc/ssl/private/secure.key"),
301
() -> {
302
System.out.print("Enter key password: ");
303
return System.console().readPassword();
304
}
305
);
306
307
// Parse PEM string directly
308
String pemKeyString = """
309
-----BEGIN PRIVATE KEY-----
310
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
311
-----END PRIVATE KEY-----
312
""";
313
PrivateKey parsedKey = PemUtils.parsePKCS8PemString(pemKeyString);
314
315
// Read multiple certificate files
316
List<Certificate> certificates = PemUtils.readCertificates(List.of(
317
Paths.get("/etc/ssl/certs/ca.pem"),
318
Paths.get("/etc/ssl/certs/intermediate.pem"),
319
Paths.get("/etc/ssl/certs/server.pem")
320
));
321
322
// Read certificate chain from single file
323
List<Certificate> certChain = PemUtils.readCertificates(List.of(
324
Paths.get("/etc/ssl/certs/fullchain.pem") // file with multiple certificates
325
));
326
```
327
328
### SSL Utilities
329
330
General SSL-related utility methods for certificate operations.
331
332
```java { .api }
333
/**
334
* SSL-related utility methods
335
*/
336
public final class SslUtil {
337
/**
338
* Calculates certificate fingerprint using specified algorithm
339
* @param certificate X509 certificate to fingerprint
340
* @param algorithm hash algorithm (e.g., "SHA-256", "SHA-1", "MD5")
341
* @return hex-encoded fingerprint string
342
* @throws CertificateEncodingException if certificate encoding fails
343
*/
344
public static String calculateFingerprint(X509Certificate certificate, String algorithm)
345
throws CertificateEncodingException;
346
}
347
```
348
349
**Usage Examples:**
350
351
```java
352
import org.elasticsearch.common.ssl.SslUtil;
353
import java.security.cert.X509Certificate;
354
355
// Calculate SHA-256 fingerprint
356
String sha256Fingerprint = SslUtil.calculateFingerprint(certificate, "SHA-256");
357
System.out.println("SHA-256: " + sha256Fingerprint);
358
359
// Calculate SHA-1 fingerprint
360
String sha1Fingerprint = SslUtil.calculateFingerprint(certificate, "SHA-1");
361
System.out.println("SHA-1: " + sha1Fingerprint);
362
363
// Calculate MD5 fingerprint (less secure, for compatibility)
364
String md5Fingerprint = SslUtil.calculateFingerprint(certificate, "MD5");
365
System.out.println("MD5: " + md5Fingerprint);
366
```
367
368
### DER Parser
369
370
Minimal ASN.1 DER decoder for PKCS#1 private keys, providing low-level parsing capabilities.
371
372
```java { .api }
373
/**
374
* Minimal ASN.1 DER decoder for PKCS#1 private keys
375
*/
376
public final class DerParser {
377
/**
378
* Creates DER parser for byte array
379
* @param bytes DER encoded bytes to parse
380
*/
381
public DerParser(byte[] bytes);
382
383
/**
384
* Reads ASN.1 object with required type check
385
* @param requiredType expected ASN.1 type constant
386
* @return parsed ASN.1 object
387
* @throws IOException if parsing fails or type doesn't match
388
*/
389
public Asn1Object readAsn1Object(int requiredType) throws IOException;
390
391
/**
392
* Reads next ASN.1 object without type checking
393
* @return parsed ASN.1 object
394
* @throws IOException if parsing fails
395
*/
396
public Asn1Object readAsn1Object() throws IOException;
397
}
398
```
399
400
### ASN.1 Object Wrapper
401
402
Wrapper for parsed ASN.1 objects with convenient access methods.
403
404
```java { .api }
405
/**
406
* Wrapper for parsed ASN.1 objects
407
*/
408
public static class Asn1Object {
409
/**
410
* Gets the ASN.1 type tag
411
* @return type tag value
412
*/
413
public int getType();
414
415
/**
416
* Gets the content length
417
* @return content length in bytes
418
*/
419
public int getLength();
420
421
/**
422
* Gets the raw content bytes
423
* @return content byte array
424
*/
425
public byte[] getValue();
426
427
/**
428
* Checks if this is a constructed (composite) object
429
* @return true if constructed
430
*/
431
public boolean isConstructed();
432
433
/**
434
* Gets parser for nested content (if constructed)
435
* @return DerParser for nested content
436
* @throws IOException if not constructed or parsing fails
437
*/
438
public DerParser getParser() throws IOException;
439
440
/**
441
* Interprets content as integer value
442
* @return BigInteger value
443
* @throws IOException if content is not a valid integer
444
*/
445
public BigInteger getInteger() throws IOException;
446
447
/**
448
* Interprets content as string value
449
* @return string value
450
* @throws IOException if content is not a valid string
451
*/
452
public String getString() throws IOException;
453
454
/**
455
* Interprets content as object identifier (OID)
456
* @return OID string (e.g., "1.2.840.113549.1.1.1")
457
* @throws IOException if content is not a valid OID
458
*/
459
public String getOid() throws IOException;
460
}
461
```
462
463
### ASN.1 Type Constants
464
465
Constants for ASN.1 type identification.
466
467
```java { .api }
468
/**
469
* ASN.1 type constants
470
*/
471
public static class Type {
472
public static final int INTEGER = 0x02;
473
public static final int OCTET_STRING = 0x04;
474
public static final int OBJECT_OID = 0x06;
475
public static final int SEQUENCE = 0x30;
476
// Additional type constants...
477
}
478
```
479
480
**Usage Examples:**
481
482
```java
483
import org.elasticsearch.common.ssl.DerParser;
484
import org.elasticsearch.common.ssl.DerParser.Asn1Object;
485
import org.elasticsearch.common.ssl.DerParser.Type;
486
import java.math.BigInteger;
487
488
// Parse DER encoded private key
489
byte[] derBytes = // ... DER encoded PKCS#1 private key bytes
490
DerParser parser = new DerParser(derBytes);
491
492
// Read outer SEQUENCE
493
Asn1Object sequence = parser.readAsn1Object(Type.SEQUENCE);
494
DerParser seqParser = sequence.getParser();
495
496
// Read version (INTEGER)
497
Asn1Object version = seqParser.readAsn1Object(Type.INTEGER);
498
BigInteger versionNum = version.getInteger();
499
500
// Read modulus (large INTEGER)
501
Asn1Object modulus = seqParser.readAsn1Object(Type.INTEGER);
502
BigInteger modulusValue = modulus.getInteger();
503
504
// Continue parsing other RSA private key components...
505
```
506
507
## Utility Patterns
508
509
### Keystore Type Detection
510
511
The library automatically detects keystore types:
512
513
```java
514
// File extensions mapped to keystore types:
515
// .jks -> JKS
516
// .p12, .pfx -> PKCS12
517
// .bks -> BKS
518
// others -> JKS (default)
519
520
String type = KeyStoreUtil.inferKeyStoreType("mystore.p12"); // "PKCS12"
521
```
522
523
### Certificate Chain Validation
524
525
When building keystores, ensure proper certificate chain ordering:
526
527
```java
528
// Correct order: end entity certificate first, then intermediates, then root
529
List<Certificate> chainInOrder = List.of(
530
endEntityCert, // certificate for private key
531
intermediateCert, // intermediate CA
532
rootCert // root CA (optional)
533
);
534
535
KeyStore keystore = KeyStoreUtil.buildKeyStore(chainInOrder, privateKey, password);
536
```
537
538
### PEM File Handling
539
540
The PEM utilities handle various PEM formats:
541
542
```java
543
// Supports multiple certificate formats:
544
// - Single certificate per file
545
// - Multiple certificates in one file (certificate chains)
546
// - Mixed certificate and key files (certificates only extracted)
547
548
// Supports multiple private key formats:
549
// - PKCS#8 (-----BEGIN PRIVATE KEY-----)
550
// - PKCS#8 encrypted (-----BEGIN ENCRYPTED PRIVATE KEY-----)
551
// - Legacy RSA format (-----BEGIN RSA PRIVATE KEY-----)
552
```