]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/runtime/PkiUtils.java
Remove static default UUID factory
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / runtime / PkiUtils.java
1 package org.argeo.cms.internal.runtime;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.io.Reader;
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;
26
27 import javax.security.auth.x500.X500Principal;
28
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;
46
47 /**
48 * Utilities around private keys and certificate, mostly wrapping BouncyCastle
49 * implementations.
50 */
51 class PkiUtils {
52 private final static CmsLog log = CmsLog.getLog(PkiUtils.class);
53
54 final static String PKCS12 = "PKCS12";
55 final static String JKS = "JKS";
56
57 static final String DEFAULT_KEYSTORE_PATH = KernelConstants.DIR_PRIVATE + '/' + CmsConstants.NODE + ".p12";
58
59 static final String DEFAULT_TRUSTSTORE_PATH = KernelConstants.DIR_PRIVATE + "/trusted.p12";
60
61 static final String DEFAULT_PEM_KEY_PATH = KernelConstants.DIR_PRIVATE + '/' + CmsConstants.NODE + ".key";
62
63 static final String DEFAULT_PEM_CERT_PATH = KernelConstants.DIR_PRIVATE + '/' + CmsConstants.NODE + ".crt";
64
65 static final String IPA_PEM_CA_CERT_PATH = "/etc/ipa/ca.crt";
66
67 static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
68
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;
72 static {
73 Security.addProvider(new BouncyCastleProvider());
74 // BouncyCastle does not store trusted certificates properly
75 // TODO report it
76 BC_SECURITY_PROVIDER = "BC";
77 SUN_SECURITY_PROVIDER = "SUN";
78 SUN_JSSE_SECURITY_PROVIDER = "SunJSSE";
79 }
80
81 public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
82 int keySize, char[] keyPassword) {
83 try {
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());
98
99 keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
100 return cert;
101 } catch (GeneralSecurityException | OperatorCreationException e) {
102 throw new RuntimeException("Cannot generate self-signed certificate", e);
103 }
104 }
105
106 public static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
107 try {
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);
112 }
113 } else {
114 store.load(null);
115 }
116 return store;
117 } catch (GeneralSecurityException | IOException e) {
118 throw new RuntimeException("Cannot load keystore " + keyStoreFile, e);
119 }
120 }
121
122 public static void saveKeyStore(Path keyStoreFile, char[] keyStorePassword, KeyStore keyStore) {
123 try {
124 try (OutputStream fis = Files.newOutputStream(keyStoreFile)) {
125 keyStore.store(fis, keyStorePassword);
126 }
127 } catch (GeneralSecurityException | IOException e) {
128 throw new RuntimeException("Cannot save keystore " + keyStoreFile, e);
129 }
130 }
131
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);
136 //
137 // PEMReader pem = new PemReader(reader, new PasswordFinder() {
138 // @Override
139 // public char[] getPassword() {
140 // return password.toCharArray();
141 // }
142 // });
143 //
144 // PrivateKey key = ((KeyPair) pem.readObject()).getPrivate();
145 //
146 // pem.close();
147 // reader.close();
148 //
149 // // Get the certificate
150 // reader = new FileReader(cerFile);
151 // pem = new PEMReader(reader);
152 //
153 // X509Certificate cert = (X509Certificate) pem.readObject();
154 //
155 // pem.close();
156 // reader.close();
157 //
158 // // Put them into a PKCS12 keystore and write it to a byte[]
159 // ByteArrayOutputStream bos = new ByteArrayOutputStream();
160 // KeyStore ks = KeyStore.getInstance("PKCS12");
161 // ks.load(null);
162 // ks.setKeyEntry("alias", (Key) key, password.toCharArray(), new java.security.cert.Certificate[] { cert });
163 // ks.store(bos, password.toCharArray());
164 // bos.close();
165 // return bos.toByteArray();
166 // }
167
168 public static void loadPrivateCertificatePem(KeyStore keyStore, String alias, Reader key, char[] keyPassword,
169 Reader cert) {
170 Objects.requireNonNull(keyStore);
171 Objects.requireNonNull(key);
172 try {
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);
178 }
179 }
180
181 public static void loadTrustedCertificatePem(KeyStore keyStore,char[] keyStorePassword, Reader cert) {
182 try {
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);
188 }
189 }
190
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;
203 } else {
204 throw new IllegalArgumentException("Unsupported format for private key");
205 }
206 return converter.getPrivateKey(privateKeyInfo);
207 } catch (IOException | OperatorCreationException | PKCSException e) {
208 throw new RuntimeException("Cannot read private key", e);
209 }
210 }
211
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);
217 return cert;
218 } catch (IOException | CertificateException e) {
219 throw new RuntimeException("Cannot read private key", e);
220 }
221 }
222
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) {
229 try {
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());
235 }
236 }
237 System.out.println((System.currentTimeMillis() - begin) + " ms");
238
239 // // String text = "a";
240 // String text =
241 // "testtesttesttesttesttesttesttesttesttesttesttesttesttesttest";
242 // try {
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");
250 // p12file.delete();
251 // if (!p12file.exists()) {
252 // keyStore.load(null);
253 // generateSelfSignedCertificate(keyStore, new X500Principal(alias),
254 // 513, password);
255 // try (OutputStream out = new FileOutputStream(p12file)) {
256 // keyStore.store(out, password);
257 // }
258 // }
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();
263 // }
264 // // KeyPair key;
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);
275 // // }
276 // // privateKey = key.getPrivate();
277 // // publicKey = key.getPublic();
278 //
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);
287 //
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();
294 // }
295
296 }
297
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)) {
304 try {
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"),
309 1024, keyPwd);
310 saveKeyStore(keyStorePath, keyStorePassword, keyStore);
311 if (log.isDebugEnabled())
312 log.debug("Created self-signed unsecure keystore " + keyStorePath);
313 } catch (Exception e) {
314 try {
315 if (Files.size(keyStorePath) == 0)
316 Files.delete(keyStorePath);
317 } catch (IOException e1) {
318 // silent
319 }
320 log.error("Cannot create keystore " + keyStorePath, e);
321 }
322 } else {
323 throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
324 }
325 }
326
327 }