11package io .lettuce .test .settings ;
22
3- import java .io .FileInputStream ;
4- import java .io .FileOutputStream ;
5- import java .io .IOException ;
3+ import io .lettuce .core .internal .LettuceStrings ;
4+ import org .testcontainers .shaded .org .bouncycastle .cert .X509v3CertificateBuilder ;
5+ import org .testcontainers .shaded .org .bouncycastle .cert .jcajce .JcaX509CertificateConverter ;
6+ import org .testcontainers .shaded .org .bouncycastle .cert .jcajce .JcaX509v3CertificateBuilder ;
7+ import org .testcontainers .shaded .org .bouncycastle .operator .ContentSigner ;
8+ import org .testcontainers .shaded .org .bouncycastle .operator .jcajce .JcaContentSignerBuilder ;
9+ import org .testcontainers .shaded .org .bouncycastle .pkcs .PKCS10CertificationRequest ;
10+ import org .testcontainers .shaded .org .bouncycastle .pkcs .PKCS10CertificationRequestBuilder ;
11+ import org .testcontainers .shaded .org .bouncycastle .pkcs .jcajce .JcaPKCS10CertificationRequest ;
12+ import org .testcontainers .shaded .org .bouncycastle .pkcs .jcajce .JcaPKCS10CertificationRequestBuilder ;
13+ import org .testcontainers .shaded .org .bouncycastle .util .io .pem .PemObject ;
14+ import org .testcontainers .shaded .org .bouncycastle .util .io .pem .PemWriter ;
15+ import sun .security .x509 .X500Name ;
16+
17+ import javax .security .auth .x500 .X500Principal ;
18+ import java .io .*;
19+ import java .math .BigInteger ;
20+ import java .nio .file .Files ;
621import java .nio .file .Path ;
722import java .nio .file .Paths ;
8- import java .security .KeyStore ;
9- import java .security .KeyStoreException ;
10- import java .security .NoSuchAlgorithmException ;
23+ import java .security .*;
1124import java .security .cert .CertificateException ;
1225import java .security .cert .CertificateFactory ;
1326import java .security .cert .X509Certificate ;
27+ import java .time .Duration ;
28+ import java .time .Instant ;
1429import java .util .ArrayList ;
30+ import java .util .Date ;
1531import java .util .List ;
1632import java .util .UUID ;
1733
@@ -27,6 +43,10 @@ public class TlsSettings {
2743
2844 private static final String TEST_TRUSTSTORE = "truststore.jks" ;
2945
46+ private static final String TEST_KEYSTORE = "keystore.jks" ;
47+
48+ private static final String PASSWORD = "changeit" ;
49+
3050 public static Path envServerCert (Path certLocation ) {
3151 return Paths .get (TEST_WORK_FOLDER , certLocation .toString (), TEST_SERVER_CERT );
3252 }
@@ -39,6 +59,14 @@ public static Path testTruststorePath(String name) {
3959 return Paths .get (TEST_WORK_FOLDER , name + '-' + TEST_TRUSTSTORE );
4060 }
4161
62+ public static Path testGenCertPath (String keystoreLocation ) {
63+ return Paths .get (TEST_WORK_FOLDER , keystoreLocation );
64+ }
65+
66+ public static Path testKeyStorePath (String keystoreLocation ) {
67+ return Paths .get (TEST_WORK_FOLDER , keystoreLocation , TEST_KEYSTORE );
68+ }
69+
4270 /**
4371 * Creates an empty truststore.
4472 *
@@ -121,4 +149,90 @@ public static Path createAndSaveTestTruststore(String trustStoreName, Path certi
121149 return createAndSaveTruststore (trustedCertPaths , trustStorePath , truststorePassword );
122150 }
123151
152+ public static void generateCertificates (String caDir , String keystoreFile ) throws Exception {
153+ createDirectories (caDir );
154+
155+ KeyPair keyPair = generateKeyPair ();
156+
157+ savePrivateKey (keyPair .getPrivate (), caDir );
158+
159+ PKCS10CertificationRequest csr = generateCSR (keyPair );
160+
161+ X509Certificate certificate = signCertificate (csr , keyPair );
162+
163+ saveCertificate (certificate , caDir );
164+
165+ createPKCS12 (keyPair .getPrivate (), certificate , keystoreFile );
166+ }
167+
168+ private static void createDirectories (String caDir ) throws IOException {
169+ Files .createDirectories (Paths .get (caDir , "private" ));
170+ Files .createDirectories (Paths .get (caDir , "certs" ));
171+ }
172+
173+ private static KeyPair generateKeyPair () throws NoSuchAlgorithmException {
174+ KeyPairGenerator keyGen = KeyPairGenerator .getInstance ("RSA" );
175+ keyGen .initialize (2048 );
176+ return keyGen .generateKeyPair ();
177+ }
178+
179+ private static void savePrivateKey (PrivateKey privateKey , String caDir ) throws Exception {
180+ String keyPath = Paths .get (caDir , "private" , "client.key.pem" ).toString ();
181+ try (PemWriter pemWriter = new PemWriter (new FileWriter (keyPath ))) {
182+ pemWriter .writeObject (new PemObject ("PRIVATE KEY" , privateKey .getEncoded ()));
183+ }
184+
185+ File keyFile = new File (keyPath );
186+ keyFile .setReadable (false , false );
187+ keyFile .setReadable (true , true );
188+ keyFile .setWritable (false , false );
189+ keyFile .setExecutable (false , false );
190+ }
191+
192+ private static PKCS10CertificationRequest generateCSR (KeyPair keyPair ) throws Exception {
193+ X500Principal subject = new X500Principal ("CN=client,O=lettuce,C=NN,ST=Unknown,L=Unknown" );
194+ PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder (subject , keyPair .getPublic ());
195+
196+ ContentSigner signer = new JcaContentSignerBuilder ("SHA256withRSA" ).build (keyPair .getPrivate ());
197+
198+ return csrBuilder .build (signer );
199+ }
200+
201+ private static X509Certificate signCertificate (PKCS10CertificationRequest csr , KeyPair keyPair ) throws Exception {
202+ org .bouncycastle .asn1 .x500 .X500Name issuerName = new org .bouncycastle .asn1 .x500 .X500Name (
203+ "CN=client,O=lettuce,C=NN,ST=Unknown,L=Unknown" );
204+
205+ BigInteger serialNumber = BigInteger .valueOf (System .currentTimeMillis ());
206+ Instant now = Instant .now ();
207+ Date startDate = Date .from (now );
208+ Date endDate = Date .from (now .plus (Duration .ofDays (375 )));
209+
210+ JcaPKCS10CertificationRequest jcaCsr = new JcaPKCS10CertificationRequest (csr );
211+
212+ X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder (org .testcontainers .shaded .org .bouncycastle .asn1 .x500 .X500Name .getInstance (issuerName ), serialNumber , startDate , endDate ,
213+ jcaCsr .getSubject (), jcaCsr .getPublicKey ());
214+
215+ ContentSigner signer = new JcaContentSignerBuilder ("SHA256withRSA" ).build (keyPair .getPrivate ());
216+
217+ return new JcaX509CertificateConverter ().getCertificate (certBuilder .build (signer ));
218+ }
219+
220+ private static void saveCertificate (X509Certificate certificate , String caDir ) throws Exception {
221+ String certPath = Paths .get (caDir , "certs" , "client.cert.pem" ).toString ();
222+ try (PemWriter pemWriter = new PemWriter (new FileWriter (certPath ))) {
223+ pemWriter .writeObject (new PemObject ("CERTIFICATE" , certificate .getEncoded ()));
224+ }
225+ }
226+
227+ private static void createPKCS12 (PrivateKey privateKey , X509Certificate certificate , String keystoreFile ) throws Exception {
228+ KeyStore keyStore = KeyStore .getInstance ("PKCS12" );
229+ keyStore .load (null , null );
230+
231+ keyStore .setKeyEntry ("client" , privateKey , PASSWORD .toCharArray (), new X509Certificate [] { certificate });
232+
233+ try (OutputStream output = Files .newOutputStream (testKeyStorePath (keystoreFile ))) {
234+ keyStore .store (output , PASSWORD .toCharArray ());
235+ }
236+ }
237+
124238}
0 commit comments