26
26
import org .bouncycastle .cert .X509CertificateHolder ;
27
27
import org .bouncycastle .openssl .PEMDecryptorProvider ;
28
28
import org .bouncycastle .openssl .PEMEncryptedKeyPair ;
29
+ import org .bouncycastle .openssl .PEMKeyPair ;
29
30
import org .bouncycastle .openssl .PEMParser ;
31
+ import org .bouncycastle .openssl .bc .BcPEMDecryptorProvider ;
30
32
import org .bouncycastle .pkcs .PKCS10CertificationRequest ;
31
33
import org .elasticsearch .cli .MockTerminal ;
32
34
import org .elasticsearch .cli .Terminal ;
59
61
import java .io .Reader ;
60
62
import java .net .InetAddress ;
61
63
import java .net .URI ;
64
+ import java .net .URISyntaxException ;
62
65
import java .nio .charset .StandardCharsets ;
63
66
import java .nio .file .FileSystem ;
64
67
import java .nio .file .FileSystems ;
74
77
import java .security .cert .X509Certificate ;
75
78
import java .security .interfaces .RSAKey ;
76
79
import java .time .temporal .ChronoUnit ;
80
+ import java .util .ArrayList ;
77
81
import java .util .Arrays ;
78
82
import java .util .Collection ;
79
83
import java .util .Collections ;
85
89
import java .util .concurrent .atomic .AtomicBoolean ;
86
90
import java .util .function .Function ;
87
91
import java .util .stream .Collectors ;
92
+ import java .util .stream .Stream ;
88
93
89
94
import javax .net .ssl .KeyManagerFactory ;
90
95
import javax .net .ssl .TrustManagerFactory ;
@@ -257,17 +262,35 @@ public void testParsingFileWithInvalidDetails() throws Exception {
257
262
assertThat (terminal .getErrorOutput (), containsString ("could not be converted to a valid DN" ));
258
263
}
259
264
260
- public void testGeneratingCsr () throws Exception {
265
+ public void testGeneratingCsrFromInstancesFile () throws Exception {
261
266
Path tempDir = initTempDir ();
262
267
Path outputFile = tempDir .resolve ("out.zip" );
268
+ MockTerminal terminal = new MockTerminal ();
269
+ final List <String > args = new ArrayList <>();
270
+
263
271
Path instanceFile = writeInstancesTo (tempDir .resolve ("instances.yml" ));
264
272
Collection <CertificateInformation > certInfos = CertificateTool .parseFile (instanceFile );
265
273
assertEquals (4 , certInfos .size ());
266
274
267
275
assertFalse (Files .exists (outputFile ));
268
276
int keySize = randomFrom (1024 , 2048 );
269
277
270
- new CertificateTool .SigningRequestCommand ().generateAndWriteCsrs (outputFile , keySize , certInfos );
278
+ final boolean encrypt = randomBoolean ();
279
+ final String password = encrypt ? randomAlphaOfLengthBetween (8 , 12 ) : null ;
280
+ if (encrypt ) {
281
+ args .add ("--pass" );
282
+ if (randomBoolean ()) {
283
+ args .add (password );
284
+ } else {
285
+ for (Object ignore : certInfos ) {
286
+ terminal .addSecretInput (password );
287
+ }
288
+ }
289
+ }
290
+
291
+ final CertificateTool .SigningRequestCommand command = new CertificateTool .SigningRequestCommand ();
292
+ final OptionSet options = command .getParser ().parse (Strings .toStringArray (args ));
293
+ command .generateAndWriteCsrs (terminal , options , outputFile , keySize , certInfos );
271
294
assertTrue (Files .exists (outputFile ));
272
295
273
296
Set <PosixFilePermission > perms = Files .getPosixFilePermissions (outputFile );
@@ -284,7 +307,6 @@ public void testGeneratingCsr() throws Exception {
284
307
assertTrue (Files .exists (zipRoot .resolve (filename )));
285
308
final Path csr = zipRoot .resolve (filename + "/" + filename + ".csr" );
286
309
assertTrue (Files .exists (csr ));
287
- assertTrue (Files .exists (zipRoot .resolve (filename + "/" + filename + ".key" )));
288
310
PKCS10CertificationRequest request = readCertificateRequest (csr );
289
311
assertEquals (certInfo .name .x500Principal .getName (), request .getSubject ().toString ());
290
312
Attribute [] extensionsReq = request .getAttributes (PKCSObjectIdentifiers .pkcs_9_at_extensionRequest );
@@ -296,7 +318,82 @@ public void testGeneratingCsr() throws Exception {
296
318
} else {
297
319
assertEquals (0 , extensionsReq .length );
298
320
}
321
+
322
+ final Path keyPath = zipRoot .resolve (filename + "/" + filename + ".key" );
323
+ assertTrue (Files .exists (keyPath ));
324
+ PEMKeyPair key = readPrivateKey (keyPath , password );
325
+ assertNotNull (key );
326
+ }
327
+ }
328
+
329
+ public void testGeneratingCsrFromCommandLineParameters () throws Exception {
330
+ Path tempDir = initTempDir ();
331
+ Path outputFile = tempDir .resolve ("out.zip" );
332
+ MockTerminal terminal = new MockTerminal ();
333
+ final List <String > args = new ArrayList <>();
334
+
335
+ final int keySize = randomFrom (1024 , 2048 );
336
+ args .add ("--keysize" );
337
+ args .add (String .valueOf (keySize ));
338
+
339
+ final String name = randomAlphaOfLengthBetween (4 , 16 );
340
+ args .add ("--name" );
341
+ args .add (name );
342
+
343
+ final List <String > dns = randomList (0 , 4 , () -> randomAlphaOfLengthBetween (4 , 8 ) + "." + randomAlphaOfLengthBetween (2 , 5 ));
344
+ dns .stream ().map (s -> "--dns=" + s ).forEach (args ::add );
345
+ final List <String > ip = randomList (
346
+ 0 ,
347
+ 2 ,
348
+ () -> Stream .generate (() -> randomIntBetween (10 , 250 )).limit (4 ).map (String ::valueOf ).collect (Collectors .joining ("." ))
349
+ );
350
+ ip .stream ().map (s -> "--ip=" + s ).forEach (args ::add );
351
+
352
+ final boolean encrypt = randomBoolean ();
353
+ final String password = encrypt ? randomAlphaOfLengthBetween (8 , 12 ) : null ;
354
+ if (encrypt ) {
355
+ args .add ("--pass" );
356
+ if (randomBoolean ()) {
357
+ args .add (password );
358
+ } else {
359
+ terminal .addSecretInput (password );
360
+ }
361
+ }
362
+
363
+ final CertificateTool .SigningRequestCommand command = new CertificateTool .SigningRequestCommand ();
364
+ final OptionSet options = command .getParser ().parse (Strings .toStringArray (args ));
365
+ command .generateAndWriteCsrs (terminal , options , outputFile );
366
+ assertTrue (Files .exists (outputFile ));
367
+
368
+ Set <PosixFilePermission > perms = Files .getPosixFilePermissions (outputFile );
369
+ assertTrue (perms .toString (), perms .contains (PosixFilePermission .OWNER_READ ));
370
+ assertTrue (perms .toString (), perms .contains (PosixFilePermission .OWNER_WRITE ));
371
+ assertEquals (perms .toString (), 2 , perms .size ());
372
+
373
+ final Path zipRoot = getRootPathOfZip (outputFile );
374
+
375
+ assertFalse (Files .exists (zipRoot .resolve ("ca" )));
376
+ assertTrue (Files .exists (zipRoot .resolve (name )));
377
+ final Path csr = zipRoot .resolve (name + "/" + name + ".csr" );
378
+ assertTrue (Files .exists (csr ));
379
+
380
+ PKCS10CertificationRequest request = readCertificateRequest (csr );
381
+ assertEquals ("CN=" + name , request .getSubject ().toString ());
382
+
383
+ Attribute [] extensionsReq = request .getAttributes (PKCSObjectIdentifiers .pkcs_9_at_extensionRequest );
384
+ if (dns .size () > 0 || ip .size () > 0 ) {
385
+ assertEquals (1 , extensionsReq .length );
386
+ Extensions extensions = Extensions .getInstance (extensionsReq [0 ].getAttributeValues ()[0 ]);
387
+ GeneralNames subjAltNames = GeneralNames .fromExtensions (extensions , Extension .subjectAlternativeName );
388
+ assertSubjAltNames (subjAltNames , ip , dns );
389
+ } else {
390
+ assertEquals (0 , extensionsReq .length );
299
391
}
392
+
393
+ final Path keyPath = zipRoot .resolve (name + "/" + name + ".key" );
394
+ assertTrue (Files .exists (keyPath ));
395
+ PEMKeyPair key = readPrivateKey (keyPath , password );
396
+ assertNotNull (key );
300
397
}
301
398
302
399
public void testGeneratingSignedPemCertificates () throws Exception {
@@ -968,19 +1065,6 @@ private int getDurationInDays(X509Certificate cert) {
968
1065
return (int ) ChronoUnit .DAYS .between (cert .getNotBefore ().toInstant (), cert .getNotAfter ().toInstant ());
969
1066
}
970
1067
971
- private void assertSubjAltNames (Certificate certificate , String ip , String dns ) throws Exception {
972
- final X509CertificateHolder holder = new X509CertificateHolder (certificate .getEncoded ());
973
- final GeneralNames names = GeneralNames .fromExtensions (holder .getExtensions (), Extension .subjectAlternativeName );
974
- final CertificateInformation certInfo = new CertificateInformation (
975
- "n" ,
976
- "n" ,
977
- Collections .singletonList (ip ),
978
- Collections .singletonList (dns ),
979
- Collections .emptyList ()
980
- );
981
- assertSubjAltNames (names , certInfo );
982
- }
983
-
984
1068
/**
985
1069
* Checks whether there are keys in {@code keyStore} that are trusted by {@code trustStore}.
986
1070
*/
@@ -1014,13 +1098,39 @@ private PKCS10CertificationRequest readCertificateRequest(Path path) throws Exce
1014
1098
}
1015
1099
}
1016
1100
1101
+ private PEMKeyPair readPrivateKey (Path path , String password ) throws Exception {
1102
+ try (Reader reader = Files .newBufferedReader (path ); PEMParser pemParser = new PEMParser (reader )) {
1103
+ Object object = pemParser .readObject ();
1104
+ if (password == null ) {
1105
+ assertThat (object , instanceOf (PEMKeyPair .class ));
1106
+ return (PEMKeyPair ) object ;
1107
+ } else {
1108
+ assertThat (object , instanceOf (PEMEncryptedKeyPair .class ));
1109
+ final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair ) object ;
1110
+ assertThat (encryptedKeyPair .getDekAlgName (), is ("AES-128-CBC" ));
1111
+ return encryptedKeyPair .decryptKeyPair (new BcPEMDecryptorProvider (password .toCharArray ()));
1112
+ }
1113
+ }
1114
+ }
1115
+
1017
1116
private X509Certificate readX509Certificate (InputStream input ) throws Exception {
1018
1117
List <Certificate > list = CertParsingUtils .readCertificates (input );
1019
1118
assertEquals (1 , list .size ());
1020
1119
assertThat (list .get (0 ), instanceOf (X509Certificate .class ));
1021
1120
return (X509Certificate ) list .get (0 );
1022
1121
}
1023
1122
1123
+ private void assertSubjAltNames (Certificate certificate , String ip , String dns ) throws Exception {
1124
+ final X509CertificateHolder holder = new X509CertificateHolder (certificate .getEncoded ());
1125
+ final GeneralNames names = GeneralNames .fromExtensions (holder .getExtensions (), Extension .subjectAlternativeName );
1126
+ assertSubjAltNames (names , Collections .singletonList (ip ), Collections .singletonList (dns ));
1127
+ }
1128
+
1129
+ private void assertSubjAltNames (GeneralNames generalNames , List <String > ip , List <String > dns ) throws Exception {
1130
+ final CertificateInformation certInfo = new CertificateInformation ("n" , "n" , ip , dns , Collections .emptyList ());
1131
+ assertSubjAltNames (generalNames , certInfo );
1132
+ }
1133
+
1024
1134
private void assertSubjAltNames (GeneralNames subjAltNames , CertificateInformation certInfo ) throws Exception {
1025
1135
final int expectedCount = certInfo .ipAddresses .size () + certInfo .dnsNames .size () + certInfo .commonNames .size ();
1026
1136
assertEquals (expectedCount , subjAltNames .getNames ().length );
@@ -1102,6 +1212,11 @@ private static Path resolvePath(String path) {
1102
1212
return PathUtils .get (path ).toAbsolutePath ();
1103
1213
}
1104
1214
1215
+ private static Path getRootPathOfZip (Path pemZip ) throws IOException , URISyntaxException {
1216
+ FileSystem zipFS = FileSystems .newFileSystem (new URI ("jar:" + pemZip .toUri ()), Collections .emptyMap ());
1217
+ return zipFS .getPath ("/" );
1218
+ }
1219
+
1105
1220
private String generateCA (Path caFile , MockTerminal terminal , Environment env ) throws Exception {
1106
1221
final int caKeySize = randomIntBetween (4 , 8 ) * 512 ;
1107
1222
final int days = randomIntBetween (7 , 1500 );
0 commit comments