]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/runtime/PkiUtils.java
Refactor deploy properties and move legacy CMS exception.
[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.KeyStoreException;
16 import java.security.PrivateKey;
17 import java.security.SecureRandom;
18 import java.security.Security;
19 import java.security.cert.Certificate;
20 import java.security.cert.CertificateException;
21 import java.security.cert.X509Certificate;
22 import java.util.Arrays;
23 import java.util.Date;
24
25 import javax.security.auth.x500.X500Principal;
26
27 import org.argeo.api.cms.CmsConstants;
28 import org.argeo.api.cms.CmsLog;
29 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
30 import org.bouncycastle.cert.X509CertificateHolder;
31 import org.bouncycastle.cert.X509v3CertificateBuilder;
32 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
33 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
34 import org.bouncycastle.jce.provider.BouncyCastleProvider;
35 import org.bouncycastle.openssl.PEMParser;
36 import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
37 import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
38 import org.bouncycastle.operator.ContentSigner;
39 import org.bouncycastle.operator.InputDecryptorProvider;
40 import org.bouncycastle.operator.OperatorCreationException;
41 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
42 import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
43 import org.bouncycastle.pkcs.PKCSException;
44
45 /**
46 * Utilities around private keys and certificate, mostly wrapping BouncyCastle
47 * implementations.
48 */
49 class PkiUtils {
50 private final static CmsLog log = CmsLog.getLog(PkiUtils.class);
51
52 public final static String PKCS12 = "PKCS12";
53 public static final String DEFAULT_KEYSTORE_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".p12";
54
55 public static final String DEFAULT_PEM_KEY_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".key";
56
57 public static final String DEFAULT_PEM_CERT_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".crt";
58
59 private final static String SECURITY_PROVIDER;
60 static {
61 Security.addProvider(new BouncyCastleProvider());
62 SECURITY_PROVIDER = "BC";
63 }
64
65 public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
66 int keySize, char[] keyPassword) {
67 try {
68 KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", SECURITY_PROVIDER);
69 kpGen.initialize(keySize, new SecureRandom());
70 KeyPair pair = kpGen.generateKeyPair();
71 Date notBefore = new Date(System.currentTimeMillis() - 10000);
72 Date notAfter = new Date(System.currentTimeMillis() + 365 * 24L * 3600 * 1000);
73 BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
74 X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(x500Principal, serial, notBefore,
75 notAfter, x500Principal, pair.getPublic());
76 ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(SECURITY_PROVIDER)
77 .build(pair.getPrivate());
78 X509Certificate cert = new JcaX509CertificateConverter().setProvider(SECURITY_PROVIDER)
79 .getCertificate(certGen.build(sigGen));
80 cert.checkValidity(new Date());
81 cert.verify(cert.getPublicKey());
82
83 keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
84 return cert;
85 } catch (GeneralSecurityException | OperatorCreationException e) {
86 throw new RuntimeException("Cannot generate self-signed certificate", e);
87 }
88 }
89
90 public static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
91 try {
92 KeyStore store = KeyStore.getInstance(keyStoreType, SECURITY_PROVIDER);
93 if (Files.exists(keyStoreFile)) {
94 try (InputStream fis = Files.newInputStream(keyStoreFile)) {
95 store.load(fis, keyStorePassword);
96 }
97 } else {
98 store.load(null);
99 }
100 return store;
101 } catch (GeneralSecurityException | IOException e) {
102 throw new RuntimeException("Cannot load keystore " + keyStoreFile, e);
103 }
104 }
105
106 public static void saveKeyStore(Path keyStoreFile, char[] keyStorePassword, KeyStore keyStore) {
107 try {
108 try (OutputStream fis = Files.newOutputStream(keyStoreFile)) {
109 keyStore.store(fis, keyStorePassword);
110 }
111 } catch (GeneralSecurityException | IOException e) {
112 throw new RuntimeException("Cannot save keystore " + keyStoreFile, e);
113 }
114 }
115
116 // public static byte[] pemToPKCS12(final String keyFile, final String cerFile, final String password)
117 // throws Exception {
118 // // Get the private key
119 // FileReader reader = new FileReader(keyFile);
120 //
121 // PEMReader pem = new PemReader(reader, new PasswordFinder() {
122 // @Override
123 // public char[] getPassword() {
124 // return password.toCharArray();
125 // }
126 // });
127 //
128 // PrivateKey key = ((KeyPair) pem.readObject()).getPrivate();
129 //
130 // pem.close();
131 // reader.close();
132 //
133 // // Get the certificate
134 // reader = new FileReader(cerFile);
135 // pem = new PEMReader(reader);
136 //
137 // X509Certificate cert = (X509Certificate) pem.readObject();
138 //
139 // pem.close();
140 // reader.close();
141 //
142 // // Put them into a PKCS12 keystore and write it to a byte[]
143 // ByteArrayOutputStream bos = new ByteArrayOutputStream();
144 // KeyStore ks = KeyStore.getInstance("PKCS12");
145 // ks.load(null);
146 // ks.setKeyEntry("alias", (Key) key, password.toCharArray(), new java.security.cert.Certificate[] { cert });
147 // ks.store(bos, password.toCharArray());
148 // bos.close();
149 // return bos.toByteArray();
150 // }
151
152 public static void loadPem(KeyStore keyStore, Reader key, char[] keyPassword, Reader cert) {
153 PrivateKey privateKey = loadPemPrivateKey(key, keyPassword);
154 X509Certificate certificate = loadPemCertificate(cert);
155 try {
156 keyStore.setKeyEntry(certificate.getSubjectX500Principal().getName(), privateKey, keyPassword,
157 new java.security.cert.Certificate[] { certificate });
158 } catch (KeyStoreException e) {
159 throw new RuntimeException("Cannot store PEM certificate", e);
160 }
161 }
162
163 public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
164 try (PEMParser pemParser = new PEMParser(reader)) {
165 JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
166 Object object = pemParser.readObject();
167 PrivateKeyInfo privateKeyInfo;
168 if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
169 if (keyPassword == null)
170 throw new IllegalArgumentException("A key password is required");
171 InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword);
172 privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv);
173 } else if (object instanceof PrivateKeyInfo) {
174 privateKeyInfo = (PrivateKeyInfo) object;
175 } else {
176 throw new IllegalArgumentException("Unsupported format for private key");
177 }
178 return converter.getPrivateKey(privateKeyInfo);
179 } catch (IOException | OperatorCreationException | PKCSException e) {
180 throw new RuntimeException("Cannot read private key", e);
181 }
182 }
183
184 public static X509Certificate loadPemCertificate(Reader reader) {
185 try (PEMParser pemParser = new PEMParser(reader)) {
186 X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
187 X509Certificate cert = new JcaX509CertificateConverter().setProvider(SECURITY_PROVIDER)
188 .getCertificate(certHolder);
189 return cert;
190 } catch (IOException | CertificateException e) {
191 throw new RuntimeException("Cannot read private key", e);
192 }
193 }
194
195 public static void main(String[] args) throws Exception {
196 final String ALGORITHM = "RSA";
197 final String provider = "BC";
198 SecureRandom secureRandom = new SecureRandom();
199 long begin = System.currentTimeMillis();
200 for (int i = 512; i < 1024; i = i + 2) {
201 try {
202 KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, provider);
203 keyGen.initialize(i, secureRandom);
204 keyGen.generateKeyPair();
205 } catch (Exception e) {
206 System.err.println(i + " : " + e.getMessage());
207 }
208 }
209 System.out.println((System.currentTimeMillis() - begin) + " ms");
210
211 // // String text = "a";
212 // String text =
213 // "testtesttesttesttesttesttesttesttesttesttesttesttesttesttest";
214 // try {
215 // System.out.println(text);
216 // PrivateKey privateKey;
217 // PublicKey publicKey;
218 // char[] password = "changeit".toCharArray();
219 // String alias = "CN=test";
220 // KeyStore keyStore = KeyStore.getInstance("pkcs12");
221 // File p12file = new File("test.p12");
222 // p12file.delete();
223 // if (!p12file.exists()) {
224 // keyStore.load(null);
225 // generateSelfSignedCertificate(keyStore, new X500Principal(alias),
226 // 513, password);
227 // try (OutputStream out = new FileOutputStream(p12file)) {
228 // keyStore.store(out, password);
229 // }
230 // }
231 // try (InputStream in = new FileInputStream(p12file)) {
232 // keyStore.load(in, password);
233 // privateKey = (PrivateKey) keyStore.getKey(alias, password);
234 // publicKey = keyStore.getCertificateChain(alias)[0].getPublicKey();
235 // }
236 // // KeyPair key;
237 // // final KeyPairGenerator keyGen =
238 // // KeyPairGenerator.getInstance(ALGORITHM);
239 // // keyGen.initialize(4096, new SecureRandom());
240 // // long begin = System.currentTimeMillis();
241 // // key = keyGen.generateKeyPair();
242 // // System.out.println((System.currentTimeMillis() - begin) + " ms");
243 // // keyStore.load(null);
244 // // keyStore.setKeyEntry("test", key.getPrivate(), password, null);
245 // // try(OutputStream out=new FileOutputStream(p12file)) {
246 // // keyStore.store(out, password);
247 // // }
248 // // privateKey = key.getPrivate();
249 // // publicKey = key.getPublic();
250 //
251 // Cipher encrypt = Cipher.getInstance(ALGORITHM);
252 // encrypt.init(Cipher.ENCRYPT_MODE, publicKey);
253 // byte[] encrypted = encrypt.doFinal(text.getBytes());
254 // String encryptedBase64 =
255 // Base64.getEncoder().encodeToString(encrypted);
256 // System.out.println(encryptedBase64);
257 // byte[] encryptedFromBase64 =
258 // Base64.getDecoder().decode(encryptedBase64);
259 //
260 // Cipher decrypt = Cipher.getInstance(ALGORITHM);
261 // decrypt.init(Cipher.DECRYPT_MODE, privateKey);
262 // byte[] decrypted = decrypt.doFinal(encryptedFromBase64);
263 // System.out.println(new String(decrypted));
264 // } catch (Exception e) {
265 // e.printStackTrace();
266 // }
267
268 }
269
270 public static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) {
271 // for (Provider provider : Security.getProviders())
272 // System.out.println(provider.getName());
273 // File keyStoreFile = keyStorePath.toFile();
274 char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length);
275 if (!Files.exists(keyStorePath)) {
276 try {
277 Files.createDirectories(keyStorePath.getParent());
278 KeyStore keyStore = getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
279 generateSelfSignedCertificate(keyStore,
280 new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
281 1024, keyPwd);
282 saveKeyStore(keyStorePath, keyStorePassword, keyStore);
283 if (log.isDebugEnabled())
284 log.debug("Created self-signed unsecure keystore " + keyStorePath);
285 } catch (Exception e) {
286 try {
287 if (Files.size(keyStorePath) == 0)
288 Files.delete(keyStorePath);
289 } catch (IOException e1) {
290 // silent
291 }
292 log.error("Cannot create keystore " + keyStorePath, e);
293 }
294 } else {
295 throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
296 }
297 }
298
299 }