001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.io.crypto.tls;
019
020import static java.util.Objects.requireNonNull;
021
022import java.io.File;
023import java.io.FileOutputStream;
024import java.io.IOException;
025import java.lang.invoke.MethodHandles;
026import java.nio.charset.StandardCharsets;
027import java.security.GeneralSecurityException;
028import java.security.KeyPair;
029import java.security.Security;
030import java.security.cert.X509Certificate;
031import java.util.Arrays;
032import org.apache.commons.io.FileUtils;
033import org.apache.hadoop.conf.Configuration;
034import org.apache.yetus.audience.InterfaceAudience;
035import org.bouncycastle.asn1.x500.X500Name;
036import org.bouncycastle.asn1.x500.X500NameBuilder;
037import org.bouncycastle.asn1.x500.style.BCStyle;
038import org.bouncycastle.asn1.x509.GeneralName;
039import org.bouncycastle.asn1.x509.GeneralNames;
040import org.bouncycastle.jce.provider.BouncyCastleProvider;
041import org.bouncycastle.operator.OperatorCreationException;
042
043/**
044 * This class simplifies the creation of certificates and private keys for SSL/TLS connections.
045 * <p/>
046 * This file has been copied from the Apache ZooKeeper project.
047 * @see <a href=
048 *      "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509TestContext.java">Base
049 *      revision</a>
050 */
051@InterfaceAudience.Private
052public final class X509TestContext {
053
054  private static final String TRUST_STORE_PREFIX = "hbase_test_ca";
055  private static final String KEY_STORE_PREFIX = "hbase_test_key";
056
057  private final File tempDir;
058  private final Configuration conf;
059
060  private X509Certificate trustStoreCertificate;
061  private final char[] trustStorePassword;
062  private KeyPair trustStoreKeyPair;
063  private File trustStoreJksFile;
064  private File trustStorePemFile;
065  private File trustStorePkcs12File;
066  private File trustStoreBcfksFile;
067
068  private KeyPair keyStoreKeyPair;
069  private X509Certificate keyStoreCertificate;
070  private final char[] keyStorePassword;
071  private File keyStoreJksFile;
072  private File keyStorePemFile;
073  private File keyStorePkcs12File;
074  private File keyStoreBcfksFile;
075
076  /**
077   * Constructor is intentionally private, use the Builder class instead.
078   * @param conf               the configuration
079   * @param tempDir            the directory in which key store and trust store temp files will be
080   *                           written.
081   * @param trustStoreKeyPair  the key pair for the trust store.
082   * @param trustStorePassword the password to protect a JKS trust store (ignored for PEM trust
083   *                           stores).
084   * @param keyStoreKeyPair    the key pair for the key store.
085   * @param keyStorePassword   the password to protect the key store private key.
086   */
087  private X509TestContext(Configuration conf, File tempDir, KeyPair trustStoreKeyPair,
088    char[] trustStorePassword, KeyPair keyStoreKeyPair, char[] keyStorePassword)
089    throws IOException, GeneralSecurityException, OperatorCreationException {
090    if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
091      throw new IllegalStateException("BC Security provider was not found");
092    }
093    this.conf = conf;
094    this.tempDir = requireNonNull(tempDir);
095    if (!tempDir.isDirectory()) {
096      throw new IllegalArgumentException("Not a directory: " + tempDir);
097    }
098
099    this.trustStoreKeyPair = trustStoreKeyPair;
100    this.trustStorePassword = requireNonNull(trustStorePassword);
101    this.keyStoreKeyPair = requireNonNull(keyStoreKeyPair);
102    this.keyStorePassword = requireNonNull(keyStorePassword);
103
104    createCertificates();
105
106    trustStorePkcs12File = null;
107    trustStorePemFile = null;
108    trustStoreJksFile = null;
109    keyStorePkcs12File = null;
110    keyStorePemFile = null;
111    keyStoreJksFile = null;
112  }
113
114  /**
115   * Used by {@link #cloneWithNewKeystoreCert(X509Certificate)}. Should set all fields except
116   * generated keystore path fields
117   */
118  private X509TestContext(File tempDir, Configuration conf, X509Certificate trustStoreCertificate,
119    char[] trustStorePassword, KeyPair trustStoreKeyPair, File trustStoreJksFile,
120    File trustStorePemFile, File trustStorePkcs12File, KeyPair keyStoreKeyPair,
121    char[] keyStorePassword, X509Certificate keyStoreCertificate) {
122    this.tempDir = tempDir;
123    this.conf = conf;
124    this.trustStoreCertificate = trustStoreCertificate;
125    this.trustStorePassword = trustStorePassword;
126    this.trustStoreKeyPair = trustStoreKeyPair;
127    this.trustStoreJksFile = trustStoreJksFile;
128    this.trustStorePemFile = trustStorePemFile;
129    this.trustStorePkcs12File = trustStorePkcs12File;
130    this.keyStoreKeyPair = keyStoreKeyPair;
131    this.keyStoreCertificate = keyStoreCertificate;
132    this.keyStorePassword = keyStorePassword;
133    keyStorePkcs12File = null;
134    keyStorePemFile = null;
135    keyStoreJksFile = null;
136  }
137
138  /**
139   * Generates a new certificate using this context's CA and keystoreKeyPair. By default, the cert
140   * will have localhost in the subjectAltNames. This can be overridden by passing one or more
141   * string arguments after the cert name. The expectation for those arguments is that they are
142   * valid DNS names.
143   */
144  public X509Certificate newCert(X500Name name, String... subjectAltNames)
145    throws GeneralSecurityException, IOException, OperatorCreationException {
146    if (subjectAltNames.length == 0) {
147      return X509TestHelpers.newCert(trustStoreCertificate, trustStoreKeyPair, name,
148        keyStoreKeyPair.getPublic());
149    }
150    GeneralName[] names = new GeneralName[subjectAltNames.length];
151    for (int i = 0; i < subjectAltNames.length; i++) {
152      names[i] = new GeneralName(GeneralName.dNSName, subjectAltNames[i]);
153    }
154    return X509TestHelpers.newCert(trustStoreCertificate, trustStoreKeyPair, name,
155      keyStoreKeyPair.getPublic(), new GeneralNames(names));
156  }
157
158  public File getTempDir() {
159    return tempDir;
160  }
161
162  public char[] getTrustStorePassword() {
163    return trustStorePassword;
164  }
165
166  /**
167   * Returns the path to the trust store file in the given format (JKS or PEM). Note that the file
168   * is created lazily, the first time this method is called. The trust store file is temporary and
169   * will be deleted on exit.
170   * @param storeFileType the store file type (JKS or PEM).
171   * @return the path to the trust store file.
172   * @throws IOException if there is an error creating the trust store file.
173   */
174  public File getTrustStoreFile(KeyStoreFileType storeFileType) throws IOException {
175    switch (storeFileType) {
176      case JKS:
177        return getTrustStoreJksFile();
178      case PEM:
179        return getTrustStorePemFile();
180      case PKCS12:
181        return getTrustStorePkcs12File();
182      case BCFKS:
183        return getTrustStoreBcfksFile();
184      default:
185        throw new IllegalArgumentException("Invalid trust store type: " + storeFileType
186          + ", must be one of: " + Arrays.toString(KeyStoreFileType.values()));
187    }
188  }
189
190  private File getTrustStoreJksFile() throws IOException {
191    if (trustStoreJksFile == null) {
192      trustStoreJksFile = File.createTempFile(TRUST_STORE_PREFIX,
193        KeyStoreFileType.JKS.getDefaultFileExtension(), tempDir);
194      trustStoreJksFile.deleteOnExit();
195      generateTrustStoreJksFile();
196    }
197    return trustStoreJksFile;
198  }
199
200  private void generateTrustStoreJksFile() throws IOException {
201    try (final FileOutputStream trustStoreOutputStream = new FileOutputStream(trustStoreJksFile)) {
202      byte[] bytes =
203        X509TestHelpers.certToJavaTrustStoreBytes(trustStoreCertificate, trustStorePassword);
204      trustStoreOutputStream.write(bytes);
205      trustStoreOutputStream.flush();
206    } catch (GeneralSecurityException e) {
207      throw new IOException(e);
208    }
209  }
210
211  private File getTrustStorePemFile() throws IOException {
212    if (trustStorePemFile == null) {
213      trustStorePemFile = File.createTempFile(TRUST_STORE_PREFIX,
214        KeyStoreFileType.PEM.getDefaultFileExtension(), tempDir);
215      trustStorePemFile.deleteOnExit();
216      generateTrustStorePemFile();
217    }
218    return trustStorePemFile;
219  }
220
221  private void generateTrustStorePemFile() throws IOException {
222    FileUtils.writeStringToFile(trustStorePemFile,
223      X509TestHelpers.pemEncodeX509Certificate(trustStoreCertificate), StandardCharsets.US_ASCII,
224      false);
225  }
226
227  private File getTrustStorePkcs12File() throws IOException {
228    if (trustStorePkcs12File == null) {
229      trustStorePkcs12File = File.createTempFile(TRUST_STORE_PREFIX,
230        KeyStoreFileType.PKCS12.getDefaultFileExtension(), tempDir);
231      trustStorePkcs12File.deleteOnExit();
232      generateTrustStorePkcs12File();
233    }
234    return trustStorePkcs12File;
235  }
236
237  private void generateTrustStorePkcs12File() throws IOException {
238    try (
239      final FileOutputStream trustStoreOutputStream = new FileOutputStream(trustStorePkcs12File)) {
240      byte[] bytes =
241        X509TestHelpers.certToPKCS12TrustStoreBytes(trustStoreCertificate, trustStorePassword);
242      trustStoreOutputStream.write(bytes);
243      trustStoreOutputStream.flush();
244    } catch (GeneralSecurityException e) {
245      throw new IOException(e);
246    }
247  }
248
249  private File getTrustStoreBcfksFile() throws IOException {
250    if (trustStoreBcfksFile == null) {
251      trustStoreBcfksFile = File.createTempFile(TRUST_STORE_PREFIX,
252        KeyStoreFileType.BCFKS.getDefaultFileExtension(), tempDir);
253      trustStoreBcfksFile.deleteOnExit();
254      generateTrustStoreBcfksFile();
255    }
256    return trustStoreBcfksFile;
257  }
258
259  private void generateTrustStoreBcfksFile() throws IOException {
260    try (
261      final FileOutputStream trustStoreOutputStream = new FileOutputStream(trustStoreBcfksFile)) {
262      byte[] bytes =
263        X509TestHelpers.certToBCFKSTrustStoreBytes(trustStoreCertificate, trustStorePassword);
264      trustStoreOutputStream.write(bytes);
265      trustStoreOutputStream.flush();
266    } catch (GeneralSecurityException e) {
267      throw new IOException(e);
268    }
269  }
270
271  public X509Certificate getKeyStoreCertificate() {
272    return keyStoreCertificate;
273  }
274
275  public char[] getKeyStorePassword() {
276    return keyStorePassword;
277  }
278
279  public boolean isKeyStoreEncrypted() {
280    return keyStorePassword != null;
281  }
282
283  public Configuration getConf() {
284    return conf;
285  }
286
287  /**
288   * Returns the path to the key store file in the given format (JKS, PEM, ...). Note that the file
289   * is created lazily, the first time this method is called. The key store file is temporary and
290   * will be deleted on exit.
291   * @param storeFileType the store file type (JKS, PEM, ...).
292   * @return the path to the key store file.
293   * @throws IOException if there is an error creating the key store file.
294   */
295  public File getKeyStoreFile(KeyStoreFileType storeFileType) throws IOException {
296    switch (storeFileType) {
297      case JKS:
298        return getKeyStoreJksFile();
299      case PEM:
300        return getKeyStorePemFile();
301      case PKCS12:
302        return getKeyStorePkcs12File();
303      case BCFKS:
304        return getKeyStoreBcfksFile();
305      default:
306        throw new IllegalArgumentException("Invalid key store type: " + storeFileType
307          + ", must be one of: " + Arrays.toString(KeyStoreFileType.values()));
308    }
309  }
310
311  private File getKeyStoreJksFile() throws IOException {
312    if (keyStoreJksFile == null) {
313      keyStoreJksFile = File.createTempFile(KEY_STORE_PREFIX,
314        KeyStoreFileType.JKS.getDefaultFileExtension(), tempDir);
315      keyStoreJksFile.deleteOnExit();
316      generateKeyStoreJksFile();
317    }
318    return keyStoreJksFile;
319  }
320
321  private void generateKeyStoreJksFile() throws IOException {
322    try (final FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStoreJksFile)) {
323      byte[] bytes = X509TestHelpers.certAndPrivateKeyToJavaKeyStoreBytes(keyStoreCertificate,
324        keyStoreKeyPair.getPrivate(), keyStorePassword);
325      keyStoreOutputStream.write(bytes);
326      keyStoreOutputStream.flush();
327    } catch (GeneralSecurityException e) {
328      throw new IOException(e);
329    }
330  }
331
332  private File getKeyStorePemFile() throws IOException {
333    if (keyStorePemFile == null) {
334      try {
335        keyStorePemFile = File.createTempFile(KEY_STORE_PREFIX,
336          KeyStoreFileType.PEM.getDefaultFileExtension(), tempDir);
337        keyStorePemFile.deleteOnExit();
338        generateKeyStorePemFile();
339      } catch (OperatorCreationException e) {
340        throw new IOException(e);
341      }
342    }
343    return keyStorePemFile;
344  }
345
346  private void generateKeyStorePemFile() throws IOException, OperatorCreationException {
347    FileUtils.writeStringToFile(keyStorePemFile,
348      X509TestHelpers.pemEncodeCertAndPrivateKey(keyStoreCertificate, keyStoreKeyPair.getPrivate(),
349        keyStorePassword),
350      StandardCharsets.US_ASCII, false);
351  }
352
353  private File getKeyStorePkcs12File() throws IOException {
354    if (keyStorePkcs12File == null) {
355      keyStorePkcs12File = File.createTempFile(KEY_STORE_PREFIX,
356        KeyStoreFileType.PKCS12.getDefaultFileExtension(), tempDir);
357      keyStorePkcs12File.deleteOnExit();
358      generateKeyStorePkcs12File();
359    }
360    return keyStorePkcs12File;
361  }
362
363  private void generateKeyStorePkcs12File() throws IOException {
364    try (final FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStorePkcs12File)) {
365      byte[] bytes = X509TestHelpers.certAndPrivateKeyToPKCS12Bytes(keyStoreCertificate,
366        keyStoreKeyPair.getPrivate(), keyStorePassword);
367      keyStoreOutputStream.write(bytes);
368      keyStoreOutputStream.flush();
369    } catch (GeneralSecurityException e) {
370      throw new IOException(e);
371    }
372  }
373
374  private File getKeyStoreBcfksFile() throws IOException {
375    if (keyStoreBcfksFile == null) {
376      keyStoreBcfksFile = File.createTempFile(KEY_STORE_PREFIX,
377        KeyStoreFileType.BCFKS.getDefaultFileExtension(), tempDir);
378      keyStoreBcfksFile.deleteOnExit();
379      generateKeyStoreBcfksFile();
380    }
381    return keyStoreBcfksFile;
382  }
383
384  private void generateKeyStoreBcfksFile() throws IOException {
385    try (final FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStoreBcfksFile)) {
386      byte[] bytes = X509TestHelpers.certAndPrivateKeyToBCFKSBytes(keyStoreCertificate,
387        keyStoreKeyPair.getPrivate(), keyStorePassword);
388      keyStoreOutputStream.write(bytes);
389      keyStoreOutputStream.flush();
390    } catch (GeneralSecurityException e) {
391      throw new IOException(e);
392    }
393  }
394
395  /**
396   * Sets the SSL system properties such that the given X509Util object can be used to create SSL
397   * Contexts that will use the trust store and key store files created by this test context.
398   * Example usage:
399   *
400   * <pre>
401   *     X509TestContext testContext = ...; // create the test context
402   *     X509Util x509Util = new QuorumX509Util();
403   *     testContext.setSystemProperties(x509Util, KeyStoreFileType.JKS, KeyStoreFileType.JKS);
404   *     // The returned context will use the key store and trust store created by the test context.
405   *     SSLContext ctx = x509Util.getDefaultSSLContext();
406   * </pre>
407   *
408   * @param keyStoreFileType   the store file type to use for the key store (JKS, PEM, ...).
409   * @param trustStoreFileType the store file type to use for the trust store (JKS, PEM, ...).
410   * @throws IOException if there is an error creating the key store file or trust store file.
411   */
412  public void setConfigurations(KeyStoreFileType keyStoreFileType,
413    KeyStoreFileType trustStoreFileType) throws IOException {
414    setKeystoreConfigurations(keyStoreFileType, conf);
415    conf.set(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION,
416      this.getTrustStoreFile(trustStoreFileType).getAbsolutePath());
417    conf.set(X509Util.TLS_CONFIG_TRUSTSTORE_PASSWORD, String.valueOf(this.getTrustStorePassword()));
418    conf.set(X509Util.TLS_CONFIG_TRUSTSTORE_TYPE, trustStoreFileType.getPropertyValue());
419  }
420
421  /**
422   * Sets the KeyStore-related SSL system properties onto the given Configuration such that X509Util
423   * can be used to create SSL Contexts using that KeyStore. This can be used in special
424   * circumstances to inject a "bad" certificate where the keystore doesn't match the CA in the
425   * truststore. Or use it to create a connection without a truststore.
426   * @see #setConfigurations(KeyStoreFileType, KeyStoreFileType) which sets both keystore and
427   *      truststore and is more applicable to general use.
428   */
429  public void setKeystoreConfigurations(KeyStoreFileType keyStoreFileType, Configuration confToSet)
430    throws IOException {
431
432    confToSet.set(X509Util.TLS_CONFIG_KEYSTORE_LOCATION,
433      this.getKeyStoreFile(keyStoreFileType).getAbsolutePath());
434    confToSet.set(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD,
435      String.valueOf(this.getKeyStorePassword()));
436    confToSet.set(X509Util.TLS_CONFIG_KEYSTORE_TYPE, keyStoreFileType.getPropertyValue());
437  }
438
439  public void clearConfigurations() {
440    conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
441    conf.unset(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
442    conf.unset(X509Util.TLS_CONFIG_KEYSTORE_TYPE);
443    conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION);
444    conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_PASSWORD);
445    conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_TYPE);
446  }
447
448  /**
449   * Creates a clone of the current context, but injecting the passed certificate as the KeyStore
450   * cert. The new context's keystore path fields are nulled, so the next call to
451   * {@link #setConfigurations(KeyStoreFileType, KeyStoreFileType)},
452   * {@link #setKeystoreConfigurations(KeyStoreFileType, Configuration)} , or
453   * {@link #getKeyStoreFile(KeyStoreFileType)} will create a new keystore with this certificate in
454   * place.
455   * @param cert the cert to replace
456   */
457  public X509TestContext cloneWithNewKeystoreCert(X509Certificate cert) {
458    return new X509TestContext(tempDir, conf, trustStoreCertificate, trustStorePassword,
459      trustStoreKeyPair, trustStoreJksFile, trustStorePemFile, trustStorePkcs12File,
460      keyStoreKeyPair, keyStorePassword, cert);
461  }
462
463  public void regenerateStores(X509KeyType keyStoreKeyType, X509KeyType trustStoreKeyType,
464    KeyStoreFileType keyStoreFileType, KeyStoreFileType trustStoreFileType)
465    throws GeneralSecurityException, IOException, OperatorCreationException {
466
467    trustStoreKeyPair = X509TestHelpers.generateKeyPair(trustStoreKeyType);
468    keyStoreKeyPair = X509TestHelpers.generateKeyPair(keyStoreKeyType);
469    createCertificates();
470
471    switch (keyStoreFileType) {
472      case JKS:
473        generateKeyStoreJksFile();
474        break;
475      case PEM:
476        generateKeyStorePemFile();
477        break;
478      case BCFKS:
479        generateKeyStoreBcfksFile();
480        break;
481      case PKCS12:
482        generateKeyStorePkcs12File();
483        break;
484    }
485
486    switch (trustStoreFileType) {
487      case JKS:
488        generateTrustStoreJksFile();
489        break;
490      case PEM:
491        generateTrustStorePemFile();
492        break;
493      case PKCS12:
494        generateTrustStorePkcs12File();
495        break;
496      case BCFKS:
497        generateTrustStoreBcfksFile();
498        break;
499    }
500  }
501
502  private void createCertificates()
503    throws GeneralSecurityException, IOException, OperatorCreationException {
504    X500NameBuilder caNameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
505    caNameBuilder.addRDN(BCStyle.CN,
506      MethodHandles.lookup().lookupClass().getCanonicalName() + " Root CA");
507    trustStoreCertificate =
508      X509TestHelpers.newSelfSignedCACert(caNameBuilder.build(), trustStoreKeyPair);
509
510    X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
511    nameBuilder.addRDN(BCStyle.CN,
512      MethodHandles.lookup().lookupClass().getCanonicalName() + " Zookeeper Test");
513    keyStoreCertificate = newCert(nameBuilder.build());
514  }
515
516  /**
517   * Builder class, used for creating new instances of X509TestContext.
518   */
519  public static class Builder {
520
521    private final Configuration conf;
522    private File tempDir;
523    private X509KeyType trustStoreKeyType;
524    private char[] trustStorePassword;
525    private X509KeyType keyStoreKeyType;
526    private char[] keyStorePassword;
527
528    /**
529     * Creates an empty builder with the given Configuration.
530     */
531    public Builder(Configuration conf) {
532      this.conf = conf;
533      trustStoreKeyType = X509KeyType.EC;
534      keyStoreKeyType = X509KeyType.EC;
535    }
536
537    /**
538     * Builds a new X509TestContext from this builder.
539     * @return a new X509TestContext
540     */
541    public X509TestContext build()
542      throws IOException, GeneralSecurityException, OperatorCreationException {
543      KeyPair trustStoreKeyPair = X509TestHelpers.generateKeyPair(trustStoreKeyType);
544      KeyPair keyStoreKeyPair = X509TestHelpers.generateKeyPair(keyStoreKeyType);
545      return new X509TestContext(conf, tempDir, trustStoreKeyPair, trustStorePassword,
546        keyStoreKeyPair, keyStorePassword);
547    }
548
549    /**
550     * Sets the temporary directory. Certificate and private key files will be created in this
551     * directory.
552     * @param tempDir the temp directory.
553     * @return this Builder.
554     */
555    public Builder setTempDir(File tempDir) {
556      this.tempDir = tempDir;
557      return this;
558    }
559
560    /**
561     * Sets the trust store key type. The CA key generated for the test context will be of this
562     * type.
563     * @param keyType the key type.
564     * @return this Builder.
565     */
566    public Builder setTrustStoreKeyType(X509KeyType keyType) {
567      trustStoreKeyType = keyType;
568      return this;
569    }
570
571    /**
572     * Sets the trust store password. Ignored for PEM trust stores, JKS trust stores will be
573     * encrypted with this password.
574     * @param password the password.
575     * @return this Builder.
576     */
577    public Builder setTrustStorePassword(char[] password) {
578      trustStorePassword = password;
579      return this;
580    }
581
582    /**
583     * Sets the key store key type. The private key generated for the test context will be of this
584     * type.
585     * @param keyType the key type.
586     * @return this Builder.
587     */
588    public Builder setKeyStoreKeyType(X509KeyType keyType) {
589      keyStoreKeyType = keyType;
590      return this;
591    }
592
593    /**
594     * Sets the key store password. The private key (PEM, JKS) and certificate (JKS only) will be
595     * encrypted with this password.
596     * @param password the password.
597     * @return this Builder.
598     */
599    public Builder setKeyStorePassword(char[] password) {
600      keyStorePassword = password;
601      return this;
602    }
603  }
604
605  /**
606   * Returns a new default-constructed Builder.
607   * @return a new Builder.
608   */
609  public static Builder newBuilder(Configuration conf) {
610    return new Builder(conf);
611  }
612}