1 package org
.argeo
.cms
.internal
.runtime
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.OutputStream
;
7 import java
.math
.BigInteger
;
8 import java
.net
.InetAddress
;
9 import java
.nio
.file
.Files
;
10 import java
.nio
.file
.Path
;
11 import java
.security
.GeneralSecurityException
;
12 import java
.security
.KeyPair
;
13 import java
.security
.KeyPairGenerator
;
14 import java
.security
.KeyStore
;
15 import java
.security
.KeyStore
.TrustedCertificateEntry
;
16 import java
.security
.KeyStoreException
;
17 import java
.security
.PrivateKey
;
18 import java
.security
.SecureRandom
;
19 import java
.security
.Security
;
20 import java
.security
.cert
.Certificate
;
21 import java
.security
.cert
.CertificateException
;
22 import java
.security
.cert
.X509Certificate
;
23 import java
.util
.Arrays
;
24 import java
.util
.Date
;
25 import java
.util
.Objects
;
27 import javax
.security
.auth
.x500
.X500Principal
;
29 import org
.argeo
.api
.cms
.CmsConstants
;
30 import org
.argeo
.api
.cms
.CmsLog
;
31 import org
.bouncycastle
.asn1
.pkcs
.PrivateKeyInfo
;
32 import org
.bouncycastle
.cert
.X509CertificateHolder
;
33 import org
.bouncycastle
.cert
.X509v3CertificateBuilder
;
34 import org
.bouncycastle
.cert
.jcajce
.JcaX509CertificateConverter
;
35 import org
.bouncycastle
.cert
.jcajce
.JcaX509v3CertificateBuilder
;
36 import org
.bouncycastle
.jce
.provider
.BouncyCastleProvider
;
37 import org
.bouncycastle
.openssl
.PEMParser
;
38 import org
.bouncycastle
.openssl
.jcajce
.JcaPEMKeyConverter
;
39 import org
.bouncycastle
.openssl
.jcajce
.JceOpenSSLPKCS8DecryptorProviderBuilder
;
40 import org
.bouncycastle
.operator
.ContentSigner
;
41 import org
.bouncycastle
.operator
.InputDecryptorProvider
;
42 import org
.bouncycastle
.operator
.OperatorCreationException
;
43 import org
.bouncycastle
.operator
.jcajce
.JcaContentSignerBuilder
;
44 import org
.bouncycastle
.pkcs
.PKCS8EncryptedPrivateKeyInfo
;
45 import org
.bouncycastle
.pkcs
.PKCSException
;
48 * Utilities around private keys and certificate, mostly wrapping BouncyCastle
52 private final static CmsLog log
= CmsLog
.getLog(PkiUtils
.class);
54 final static String PKCS12
= "PKCS12";
55 final static String JKS
= "JKS";
57 static final String DEFAULT_KEYSTORE_PATH
= KernelConstants
.DIR_PRIVATE
+ '/' + CmsConstants
.NODE
+ ".p12";
59 static final String DEFAULT_TRUSTSTORE_PATH
= KernelConstants
.DIR_PRIVATE
+ "/trusted.p12";
61 static final String DEFAULT_PEM_KEY_PATH
= KernelConstants
.DIR_PRIVATE
+ '/' + CmsConstants
.NODE
+ ".key";
63 static final String DEFAULT_PEM_CERT_PATH
= KernelConstants
.DIR_PRIVATE
+ '/' + CmsConstants
.NODE
+ ".crt";
65 static final String IPA_PEM_CA_CERT_PATH
= "/etc/ipa/ca.crt";
67 static final String DEFAULT_KEYSTORE_PASSWORD
= "changeit";
69 private final static String SUN_SECURITY_PROVIDER
;
70 private final static String SUN_JSSE_SECURITY_PROVIDER
;
71 private final static String BC_SECURITY_PROVIDER
;
73 Security
.addProvider(new BouncyCastleProvider());
74 // BouncyCastle does not store trusted certificates properly
76 BC_SECURITY_PROVIDER
= "BC";
77 SUN_SECURITY_PROVIDER
= "SUN";
78 SUN_JSSE_SECURITY_PROVIDER
= "SunJSSE";
81 public static X509Certificate
generateSelfSignedCertificate(KeyStore keyStore
, X500Principal x500Principal
,
82 int keySize
, char[] keyPassword
) {
84 KeyPairGenerator kpGen
= KeyPairGenerator
.getInstance("RSA", BC_SECURITY_PROVIDER
);
85 kpGen
.initialize(keySize
, new SecureRandom());
86 KeyPair pair
= kpGen
.generateKeyPair();
87 Date notBefore
= new Date(System
.currentTimeMillis() - 10000);
88 Date notAfter
= new Date(System
.currentTimeMillis() + 365 * 24L * 3600 * 1000);
89 BigInteger serial
= BigInteger
.valueOf(System
.currentTimeMillis());
90 X509v3CertificateBuilder certGen
= new JcaX509v3CertificateBuilder(x500Principal
, serial
, notBefore
,
91 notAfter
, x500Principal
, pair
.getPublic());
92 ContentSigner sigGen
= new JcaContentSignerBuilder("SHA256WithRSAEncryption")
93 .setProvider(BC_SECURITY_PROVIDER
).build(pair
.getPrivate());
94 X509Certificate cert
= new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER
)
95 .getCertificate(certGen
.build(sigGen
));
96 cert
.checkValidity(new Date());
97 cert
.verify(cert
.getPublicKey());
99 keyStore
.setKeyEntry(x500Principal
.getName(), pair
.getPrivate(), keyPassword
, new Certificate
[] { cert
});
101 } catch (GeneralSecurityException
| OperatorCreationException e
) {
102 throw new RuntimeException("Cannot generate self-signed certificate", e
);
106 public static KeyStore
getKeyStore(Path keyStoreFile
, char[] keyStorePassword
, String keyStoreType
) {
108 KeyStore store
= KeyStore
.getInstance(keyStoreType
, SUN_JSSE_SECURITY_PROVIDER
);
109 if (Files
.exists(keyStoreFile
)) {
110 try (InputStream fis
= Files
.newInputStream(keyStoreFile
)) {
111 store
.load(fis
, keyStorePassword
);
117 } catch (GeneralSecurityException
| IOException e
) {
118 throw new RuntimeException("Cannot load keystore " + keyStoreFile
, e
);
122 public static void saveKeyStore(Path keyStoreFile
, char[] keyStorePassword
, KeyStore keyStore
) {
124 try (OutputStream fis
= Files
.newOutputStream(keyStoreFile
)) {
125 keyStore
.store(fis
, keyStorePassword
);
127 } catch (GeneralSecurityException
| IOException e
) {
128 throw new RuntimeException("Cannot save keystore " + keyStoreFile
, e
);
132 // public static byte[] pemToPKCS12(final String keyFile, final String cerFile, final String password)
133 // throws Exception {
134 // // Get the private key
135 // FileReader reader = new FileReader(keyFile);
137 // PEMReader pem = new PemReader(reader, new PasswordFinder() {
139 // public char[] getPassword() {
140 // return password.toCharArray();
144 // PrivateKey key = ((KeyPair) pem.readObject()).getPrivate();
149 // // Get the certificate
150 // reader = new FileReader(cerFile);
151 // pem = new PEMReader(reader);
153 // X509Certificate cert = (X509Certificate) pem.readObject();
158 // // Put them into a PKCS12 keystore and write it to a byte[]
159 // ByteArrayOutputStream bos = new ByteArrayOutputStream();
160 // KeyStore ks = KeyStore.getInstance("PKCS12");
162 // ks.setKeyEntry("alias", (Key) key, password.toCharArray(), new java.security.cert.Certificate[] { cert });
163 // ks.store(bos, password.toCharArray());
165 // return bos.toByteArray();
168 public static void loadPrivateCertificatePem(KeyStore keyStore
, String alias
, Reader key
, char[] keyPassword
,
170 Objects
.requireNonNull(keyStore
);
171 Objects
.requireNonNull(key
);
173 X509Certificate certificate
= loadPemCertificate(cert
);
174 PrivateKey privateKey
= loadPemPrivateKey(key
, keyPassword
);
175 keyStore
.setKeyEntry(alias
, privateKey
, keyPassword
, new java
.security
.cert
.Certificate
[] { certificate
});
176 } catch (KeyStoreException e
) {
177 throw new RuntimeException("Cannot store PEM certificate", e
);
181 public static void loadTrustedCertificatePem(KeyStore keyStore
,char[] keyStorePassword
, Reader cert
) {
183 X509Certificate certificate
= loadPemCertificate(cert
);
184 TrustedCertificateEntry trustedCertificateEntry
= new TrustedCertificateEntry(certificate
);
185 keyStore
.setEntry(certificate
.getSubjectX500Principal().getName(), trustedCertificateEntry
, null);
186 } catch (KeyStoreException e
) {
187 throw new RuntimeException("Cannot store PEM certificate", e
);
191 public static PrivateKey
loadPemPrivateKey(Reader reader
, char[] keyPassword
) {
192 try (PEMParser pemParser
= new PEMParser(reader
)) {
193 JcaPEMKeyConverter converter
= new JcaPEMKeyConverter().setProvider(BC_SECURITY_PROVIDER
);
194 Object object
= pemParser
.readObject();
195 PrivateKeyInfo privateKeyInfo
;
196 if (object
instanceof PKCS8EncryptedPrivateKeyInfo
) {
197 if (keyPassword
== null)
198 throw new IllegalArgumentException("A key password is required");
199 InputDecryptorProvider decProv
= new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword
);
200 privateKeyInfo
= ((PKCS8EncryptedPrivateKeyInfo
) object
).decryptPrivateKeyInfo(decProv
);
201 } else if (object
instanceof PrivateKeyInfo
) {
202 privateKeyInfo
= (PrivateKeyInfo
) object
;
204 throw new IllegalArgumentException("Unsupported format for private key");
206 return converter
.getPrivateKey(privateKeyInfo
);
207 } catch (IOException
| OperatorCreationException
| PKCSException e
) {
208 throw new RuntimeException("Cannot read private key", e
);
212 public static X509Certificate
loadPemCertificate(Reader reader
) {
213 try (PEMParser pemParser
= new PEMParser(reader
)) {
214 X509CertificateHolder certHolder
= (X509CertificateHolder
) pemParser
.readObject();
215 X509Certificate cert
= new JcaX509CertificateConverter().setProvider(SUN_SECURITY_PROVIDER
)
216 .getCertificate(certHolder
);
218 } catch (IOException
| CertificateException e
) {
219 throw new RuntimeException("Cannot read private key", e
);
223 public static void main(String
[] args
) throws Exception
{
224 final String ALGORITHM
= "RSA";
225 final String provider
= "BC";
226 SecureRandom secureRandom
= new SecureRandom();
227 long begin
= System
.currentTimeMillis();
228 for (int i
= 512; i
< 1024; i
= i
+ 2) {
230 KeyPairGenerator keyGen
= KeyPairGenerator
.getInstance(ALGORITHM
, provider
);
231 keyGen
.initialize(i
, secureRandom
);
232 keyGen
.generateKeyPair();
233 } catch (Exception e
) {
234 System
.err
.println(i
+ " : " + e
.getMessage());
237 System
.out
.println((System
.currentTimeMillis() - begin
) + " ms");
239 // // String text = "a";
241 // "testtesttesttesttesttesttesttesttesttesttesttesttesttesttest";
243 // System.out.println(text);
244 // PrivateKey privateKey;
245 // PublicKey publicKey;
246 // char[] password = "changeit".toCharArray();
247 // String alias = "CN=test";
248 // KeyStore keyStore = KeyStore.getInstance("pkcs12");
249 // File p12file = new File("test.p12");
251 // if (!p12file.exists()) {
252 // keyStore.load(null);
253 // generateSelfSignedCertificate(keyStore, new X500Principal(alias),
255 // try (OutputStream out = new FileOutputStream(p12file)) {
256 // keyStore.store(out, password);
259 // try (InputStream in = new FileInputStream(p12file)) {
260 // keyStore.load(in, password);
261 // privateKey = (PrivateKey) keyStore.getKey(alias, password);
262 // publicKey = keyStore.getCertificateChain(alias)[0].getPublicKey();
265 // // final KeyPairGenerator keyGen =
266 // // KeyPairGenerator.getInstance(ALGORITHM);
267 // // keyGen.initialize(4096, new SecureRandom());
268 // // long begin = System.currentTimeMillis();
269 // // key = keyGen.generateKeyPair();
270 // // System.out.println((System.currentTimeMillis() - begin) + " ms");
271 // // keyStore.load(null);
272 // // keyStore.setKeyEntry("test", key.getPrivate(), password, null);
273 // // try(OutputStream out=new FileOutputStream(p12file)) {
274 // // keyStore.store(out, password);
276 // // privateKey = key.getPrivate();
277 // // publicKey = key.getPublic();
279 // Cipher encrypt = Cipher.getInstance(ALGORITHM);
280 // encrypt.init(Cipher.ENCRYPT_MODE, publicKey);
281 // byte[] encrypted = encrypt.doFinal(text.getBytes());
282 // String encryptedBase64 =
283 // Base64.getEncoder().encodeToString(encrypted);
284 // System.out.println(encryptedBase64);
285 // byte[] encryptedFromBase64 =
286 // Base64.getDecoder().decode(encryptedBase64);
288 // Cipher decrypt = Cipher.getInstance(ALGORITHM);
289 // decrypt.init(Cipher.DECRYPT_MODE, privateKey);
290 // byte[] decrypted = decrypt.doFinal(encryptedFromBase64);
291 // System.out.println(new String(decrypted));
292 // } catch (Exception e) {
293 // e.printStackTrace();
298 public static void createSelfSignedKeyStore(Path keyStorePath
, char[] keyStorePassword
, String keyStoreType
) {
299 // for (Provider provider : Security.getProviders())
300 // System.out.println(provider.getName());
301 // File keyStoreFile = keyStorePath.toFile();
302 char[] keyPwd
= Arrays
.copyOf(keyStorePassword
, keyStorePassword
.length
);
303 if (!Files
.exists(keyStorePath
)) {
305 Files
.createDirectories(keyStorePath
.getParent());
306 KeyStore keyStore
= getKeyStore(keyStorePath
, keyStorePassword
, keyStoreType
);
307 generateSelfSignedCertificate(keyStore
,
308 new X500Principal("CN=" + InetAddress
.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
310 saveKeyStore(keyStorePath
, keyStorePassword
, keyStore
);
311 if (log
.isDebugEnabled())
312 log
.debug("Created self-signed unsecure keystore " + keyStorePath
);
313 } catch (Exception e
) {
315 if (Files
.size(keyStorePath
) == 0)
316 Files
.delete(keyStorePath
);
317 } catch (IOException e1
) {
320 log
.error("Cannot create keystore " + keyStorePath
, e
);
323 throw new IllegalStateException("Keystore " + keyStorePath
+ " already exists");