22
33import java .io .*;
44import java .math .BigInteger ;
5+ import java .nio .file .Path ;
56import java .security .*;
67import java .security .cert .Certificate ;
78import java .security .cert .CertificateException ;
2526 * Certificate Authority for generating SSL certificates on-the-fly for HTTPS interception.
2627 *
2728 * <p>This class manages a root CA certificate and generates host-specific certificates signed by
28- * that CA. The root CA certificate is stored in the current directory and reused across restarts.
29+ * that CA. The root CA certificate is stored in a configurable directory and reused across
30+ * restarts.
2931 */
3032public class CertificateAuthority {
3133 private static final Logger logger = LoggerFactory .getLogger (CertificateAuthority .class );
3234
3335 private static final String CA_ALIAS = "tproxy-ca" ;
34- private static final String CA_KEYSTORE_FILE = "tproxy-ca.p12" ;
35- private static final String CA_CERT_FILE = "tproxy-ca.crt" ;
36+ private static final String CA_KEYSTORE_FILENAME = "tproxy-ca.p12" ;
37+ private static final String CA_CERT_FILENAME = "tproxy-ca.crt" ;
3638 private static final char [] KEYSTORE_PASSWORD = "changeit" .toCharArray ();
3739 private static final String SIGNATURE_ALGORITHM = "SHA256withRSA" ;
3840 private static final int KEY_SIZE = 2048 ;
@@ -50,17 +52,30 @@ public class CertificateAuthority {
5052 private final PrivateKey caPrivateKey ;
5153
5254 /**
53- * Create a new Certificate Authority. If a CA keystore exists in the current directory, it will
54- * be loaded. Otherwise, a new CA certificate will be generated.
55+ * Create a new Certificate Authority using the current directory for storage. If a CA keystore
56+ * exists, it will be loaded. Otherwise, a new CA certificate will be generated.
5557 *
5658 * @throws IOException if there is an error loading or creating the CA
5759 */
5860 public CertificateAuthority () throws IOException {
61+ this (Path .of ("." ));
62+ }
63+
64+ /**
65+ * Create a new Certificate Authority using the specified directory for storage. If a CA
66+ * keystore exists in the directory, it will be loaded. Otherwise, a new CA certificate will be
67+ * generated.
68+ *
69+ * @param storageDir the directory in which to store the CA keystore and certificate files
70+ * @throws IOException if there is an error loading or creating the CA
71+ */
72+ public CertificateAuthority (Path storageDir ) throws IOException {
5973 try {
60- File keystoreFile = new File (CA_KEYSTORE_FILE );
74+ File keystoreFile = storageDir .resolve (CA_KEYSTORE_FILENAME ).toFile ();
75+ File certFile = storageDir .resolve (CA_CERT_FILENAME ).toFile ();
6176
6277 if (keystoreFile .exists ()) {
63- logger .info ("Loading existing CA from {}" , CA_KEYSTORE_FILE );
78+ logger .info ("Loading existing CA from {}" , keystoreFile );
6479 caKeyStore = loadKeyStore (keystoreFile );
6580 } else {
6681 logger .info ("Generating new CA certificate" );
@@ -77,8 +92,8 @@ public CertificateAuthority() throws IOException {
7792 }
7893
7994 // Export CA certificate if it was just generated
80- if (!keystoreFile . exists () || ! new File ( CA_CERT_FILE ) .exists ()) {
81- exportCACertificate (caCertificate );
95+ if (!certFile .exists ()) {
96+ exportCACertificate (caCertificate , certFile );
8297 }
8398
8499 logger .info ("CA initialized: {}" , caCertificate .getSubjectX500Principal ());
@@ -177,7 +192,7 @@ public KeyStore generateServerCertificate(String hostname) throws IOException {
177192 *
178193 * @return the CA certificate
179194 */
180- public X509Certificate getCACertificate () {
195+ public X509Certificate caCertificate () {
181196 return caCertificate ;
182197 }
183198
@@ -260,8 +275,7 @@ private void saveKeyStore(KeyStore keyStore, File file) throws Exception {
260275 }
261276
262277 /** Export CA certificate to PEM file for easy import into browsers. */
263- private void exportCACertificate (X509Certificate cert ) throws Exception {
264- File certFile = new File (CA_CERT_FILE );
278+ private void exportCACertificate (X509Certificate cert , File certFile ) throws Exception {
265279 try (FileWriter fw = new FileWriter (certFile )) {
266280 fw .write ("-----BEGIN CERTIFICATE-----\n " );
267281 fw .write (
0 commit comments