]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.lib.sshd/src/org/argeo/cms/bc/BcUtils.java
Merge tag 'v2.3.17' into testing
[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.lang.reflect.InvocationTargetException;
8 import java.math.BigInteger;
9 import java.net.InetAddress;
10 import java.nio.file.Files;
11 import java.nio.file.Path;
12 import java.security.GeneralSecurityException;
13 import java.security.KeyPair;
14 import java.security.KeyPairGenerator;
15 import java.security.KeyStore;
16 import java.security.PrivateKey;
17 import java.security.Provider;
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
26 import javax.security.auth.x500.X500Principal;
27
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.openssl.PEMParser;
35 import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
36 import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
37 import org.bouncycastle.operator.ContentSigner;
38 import org.bouncycastle.operator.InputDecryptorProvider;
39 import org.bouncycastle.operator.OperatorCreationException;
40 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
41 import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
42 import org.bouncycastle.pkcs.PKCSException;
43
44 /** Utilities around the BouncyCastle crypto library. */
45 public class BcUtils {
46 private final static CmsLog log = CmsLog.getLog(BcUtils.class);
47
48 private final static String BC_SECURITY_PROVIDER_FIPS = "BCFIPS";
49 // private final static String BC_SECURITY_PROVIDER_NON_FIPS = "BC";
50 public final static String BC_SECURITY_PROVIDER;
51 static {
52 Class<?> clss = null;
53 try {
54 clss = Class.forName("org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider");
55 } catch (ClassNotFoundException e) {
56 log.warn("Bouncy Castle FIPS provider could not be initialised,"
57 + " we assume the non-FIPS provider is configured externally. (" + e + ")");
58 try {
59 clss = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
60 } catch (ClassNotFoundException e1) {
61 // silent
62 }
63 }
64 if (clss != null) {
65 try {
66 Provider provider = (Provider) clss.getDeclaredConstructor().newInstance();
67 Security.addProvider(provider);
68 BC_SECURITY_PROVIDER = provider.getName();
69 } catch (IllegalAccessException | InstantiationException | IllegalArgumentException
70 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
71 throw new IllegalStateException("Cannot load Bouncy Castle provider " + clss, e);
72 }
73 } else {
74 throw new IllegalStateException("Cannot load any Bouncy Castle provider");
75 }
76 }
77
78 public static boolean isFipsProvider() {
79 return BC_SECURITY_PROVIDER.equals(BC_SECURITY_PROVIDER_FIPS);
80 }
81
82 /*
83 * openssl req -x509 -newkey rsa:3072 -keyout node.key -out node.crt -sha256 -days 365 -nodes -subj "/O=UNSECURE/OU=UNSECURE/CN=$(hostname)"
84 */
85 public static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) {
86 // for (Provider provider : Security.getProviders())
87 // System.out.println(provider.getName());
88 // File keyStoreFile = keyStorePath.toFile();
89 char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length);
90 if (!Files.exists(keyStorePath)) {
91 try {
92 Files.createDirectories(keyStorePath.getParent());
93 KeyStore keyStore = getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
94 generateSelfSignedCertificate(keyStore,
95 new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
96 3072, keyPwd);
97 saveKeyStore(keyStorePath, keyStorePassword, keyStore);
98 if (log.isDebugEnabled())
99 log.debug("Created self-signed unsecure keystore " + keyStorePath);
100 } catch (Exception e) {
101 try {
102 if (Files.size(keyStorePath) == 0)
103 Files.delete(keyStorePath);
104 } catch (IOException e1) {
105 // silent
106 }
107 log.error("Cannot create keystore " + keyStorePath, e);
108 }
109 } else {
110 throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
111 }
112 }
113
114 public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
115 int keySize, char[] keyPassword) {
116 try {
117 KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC_SECURITY_PROVIDER);
118 kpGen.initialize(keySize, new SecureRandom());
119 KeyPair pair = kpGen.generateKeyPair();
120 Date notBefore = new Date(System.currentTimeMillis() - 10000);
121 Date notAfter = new Date(System.currentTimeMillis() + 365 * 24L * 3600 * 1000);
122 BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
123 X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(x500Principal, serial, notBefore,
124 notAfter, x500Principal, pair.getPublic());
125 ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
126 .setProvider(BC_SECURITY_PROVIDER).build(pair.getPrivate());
127 X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER)
128 .getCertificate(certGen.build(sigGen));
129 cert.checkValidity(new Date());
130 cert.verify(cert.getPublicKey());
131
132 keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
133 return cert;
134 } catch (GeneralSecurityException | OperatorCreationException e) {
135 throw new RuntimeException("Cannot generate self-signed certificate", e);
136 }
137 }
138
139 public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
140 try (PEMParser pemParser = new PEMParser(reader)) {
141 JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BC_SECURITY_PROVIDER);
142 Object object = pemParser.readObject();
143 PrivateKeyInfo privateKeyInfo;
144 if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
145 if (keyPassword == null)
146 throw new IllegalArgumentException("A key password is required");
147 InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword);
148 privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv);
149 } else if (object instanceof PrivateKeyInfo) {
150 privateKeyInfo = (PrivateKeyInfo) object;
151 } else {
152 throw new IllegalArgumentException("Unsupported format for private key");
153 }
154 return converter.getPrivateKey(privateKeyInfo);
155 } catch (IOException | OperatorCreationException | PKCSException e) {
156 throw new RuntimeException("Cannot read private key", e);
157 }
158 }
159
160 public static X509Certificate loadPemCertificate(Reader reader) {
161 try (PEMParser pemParser = new PEMParser(reader)) {
162 X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
163 X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER)
164 .getCertificate(certHolder);
165 return cert;
166 } catch (IOException | CertificateException e) {
167 throw new RuntimeException("Cannot read private key", e);
168 }
169 }
170
171 private static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
172 try {
173 KeyStore store = KeyStore.getInstance(keyStoreType, BC_SECURITY_PROVIDER);
174 if (Files.exists(keyStoreFile)) {
175 try (InputStream fis = Files.newInputStream(keyStoreFile)) {
176 store.load(fis, keyStorePassword);
177 }
178 } else {
179 store.load(null);
180 }
181 return store;
182 } catch (GeneralSecurityException | IOException e) {
183 throw new RuntimeException("Cannot load keystore " + keyStoreFile, e);
184 }
185 }
186
187 private static void saveKeyStore(Path keyStoreFile, char[] keyStorePassword, KeyStore keyStore) {
188 try {
189 try (OutputStream fis = Files.newOutputStream(keyStoreFile)) {
190 keyStore.store(fis, keyStorePassword);
191 }
192 } catch (GeneralSecurityException | IOException e) {
193 throw new RuntimeException("Cannot save keystore " + keyStoreFile, e);
194 }
195 }
196
197 /** singleton */
198 private BcUtils() {
199 }
200
201 // public static void main(String args[]) {
202 // createSelfSignedKeyStore(Paths.get("./selfsigned.p12"), "demo".toCharArray(), "PKCS12");
203 // }
204 }