]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.lib.sshd/src/org/argeo/cms/bc/BcUtils.java
Protect against mutability of LdapName
[lgpl/argeo-commons.git] / org.argeo.cms.lib.sshd / src / org / argeo / cms / bc / BcUtils.java
1 package org.argeo.cms.bc;
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.PrivateKey;
16 import java.security.SecureRandom;
17 import java.security.Security;
18 import java.security.cert.Certificate;
19 import java.security.cert.CertificateException;
20 import java.security.cert.X509Certificate;
21 import java.util.Arrays;
22 import java.util.Date;
23
24 import javax.security.auth.x500.X500Principal;
25
26 import org.argeo.api.cms.CmsLog;
27 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
28 import org.bouncycastle.cert.X509CertificateHolder;
29 import org.bouncycastle.cert.X509v3CertificateBuilder;
30 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
31 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
32 import org.bouncycastle.jce.provider.BouncyCastleProvider;
33 import org.bouncycastle.openssl.PEMParser;
34 import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
35 import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
36 import org.bouncycastle.operator.ContentSigner;
37 import org.bouncycastle.operator.InputDecryptorProvider;
38 import org.bouncycastle.operator.OperatorCreationException;
39 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
40 import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
41 import org.bouncycastle.pkcs.PKCSException;
42
43 /** Utilities around the BouncyCastle crypto library. */
44 public class BcUtils {
45 private final static CmsLog log = CmsLog.getLog(BcUtils.class);
46
47 private final static String BC_SECURITY_PROVIDER;
48 static {
49 Security.addProvider(new BouncyCastleProvider());
50 BC_SECURITY_PROVIDER = "BC";
51 }
52
53 public static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) {
54 // for (Provider provider : Security.getProviders())
55 // System.out.println(provider.getName());
56 // File keyStoreFile = keyStorePath.toFile();
57 char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length);
58 if (!Files.exists(keyStorePath)) {
59 try {
60 Files.createDirectories(keyStorePath.getParent());
61 KeyStore keyStore = getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
62 generateSelfSignedCertificate(keyStore,
63 new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
64 1024, keyPwd);
65 saveKeyStore(keyStorePath, keyStorePassword, keyStore);
66 if (log.isDebugEnabled())
67 log.debug("Created self-signed unsecure keystore " + keyStorePath);
68 } catch (Exception e) {
69 try {
70 if (Files.size(keyStorePath) == 0)
71 Files.delete(keyStorePath);
72 } catch (IOException e1) {
73 // silent
74 }
75 log.error("Cannot create keystore " + keyStorePath, e);
76 }
77 } else {
78 throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
79 }
80 }
81
82 public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
83 int keySize, char[] keyPassword) {
84 try {
85 KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC_SECURITY_PROVIDER);
86 kpGen.initialize(keySize, new SecureRandom());
87 KeyPair pair = kpGen.generateKeyPair();
88 Date notBefore = new Date(System.currentTimeMillis() - 10000);
89 Date notAfter = new Date(System.currentTimeMillis() + 365 * 24L * 3600 * 1000);
90 BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
91 X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(x500Principal, serial, notBefore,
92 notAfter, x500Principal, pair.getPublic());
93 ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
94 .setProvider(BC_SECURITY_PROVIDER).build(pair.getPrivate());
95 X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER)
96 .getCertificate(certGen.build(sigGen));
97 cert.checkValidity(new Date());
98 cert.verify(cert.getPublicKey());
99
100 keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
101 return cert;
102 } catch (GeneralSecurityException | OperatorCreationException e) {
103 throw new RuntimeException("Cannot generate self-signed certificate", e);
104 }
105 }
106
107 public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
108 try (PEMParser pemParser = new PEMParser(reader)) {
109 JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BC_SECURITY_PROVIDER);
110 Object object = pemParser.readObject();
111 PrivateKeyInfo privateKeyInfo;
112 if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
113 if (keyPassword == null)
114 throw new IllegalArgumentException("A key password is required");
115 InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword);
116 privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv);
117 } else if (object instanceof PrivateKeyInfo) {
118 privateKeyInfo = (PrivateKeyInfo) object;
119 } else {
120 throw new IllegalArgumentException("Unsupported format for private key");
121 }
122 return converter.getPrivateKey(privateKeyInfo);
123 } catch (IOException | OperatorCreationException | PKCSException e) {
124 throw new RuntimeException("Cannot read private key", e);
125 }
126 }
127
128 public static X509Certificate loadPemCertificate(Reader reader) {
129 try (PEMParser pemParser = new PEMParser(reader)) {
130 X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
131 X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER)
132 .getCertificate(certHolder);
133 return cert;
134 } catch (IOException | CertificateException e) {
135 throw new RuntimeException("Cannot read private key", e);
136 }
137 }
138
139 private static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
140 try {
141 KeyStore store = KeyStore.getInstance(keyStoreType, BC_SECURITY_PROVIDER);
142 if (Files.exists(keyStoreFile)) {
143 try (InputStream fis = Files.newInputStream(keyStoreFile)) {
144 store.load(fis, keyStorePassword);
145 }
146 } else {
147 store.load(null);
148 }
149 return store;
150 } catch (GeneralSecurityException | IOException e) {
151 throw new RuntimeException("Cannot load keystore " + keyStoreFile, e);
152 }
153 }
154
155 private static void saveKeyStore(Path keyStoreFile, char[] keyStorePassword, KeyStore keyStore) {
156 try {
157 try (OutputStream fis = Files.newOutputStream(keyStoreFile)) {
158 keyStore.store(fis, keyStorePassword);
159 }
160 } catch (GeneralSecurityException | IOException e) {
161 throw new RuntimeException("Cannot save keystore " + keyStoreFile, e);
162 }
163 }
164
165 /** singleton */
166 private BcUtils() {
167 }
168 }