--- /dev/null
+package org.argeo.cms.bc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.argeo.api.cms.CmsLog;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.InputDecryptorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.pkcs.PKCSException;
+
+/** Utilities around the BouncyCastle crypto library. */
+public class BcUtils {
+ private final static CmsLog log = CmsLog.getLog(BcUtils.class);
+
+ private final static String BC_SECURITY_PROVIDER;
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ BC_SECURITY_PROVIDER = "BC";
+ }
+
+ public static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) {
+ // for (Provider provider : Security.getProviders())
+ // System.out.println(provider.getName());
+ // File keyStoreFile = keyStorePath.toFile();
+ char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length);
+ if (!Files.exists(keyStorePath)) {
+ try {
+ Files.createDirectories(keyStorePath.getParent());
+ KeyStore keyStore = getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
+ generateSelfSignedCertificate(keyStore,
+ new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
+ 1024, keyPwd);
+ saveKeyStore(keyStorePath, keyStorePassword, keyStore);
+ if (log.isDebugEnabled())
+ log.debug("Created self-signed unsecure keystore " + keyStorePath);
+ } catch (Exception e) {
+ try {
+ if (Files.size(keyStorePath) == 0)
+ Files.delete(keyStorePath);
+ } catch (IOException e1) {
+ // silent
+ }
+ log.error("Cannot create keystore " + keyStorePath, e);
+ }
+ } else {
+ throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
+ }
+ }
+
+ public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
+ int keySize, char[] keyPassword) {
+ try {
+ KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC_SECURITY_PROVIDER);
+ kpGen.initialize(keySize, new SecureRandom());
+ KeyPair pair = kpGen.generateKeyPair();
+ Date notBefore = new Date(System.currentTimeMillis() - 10000);
+ Date notAfter = new Date(System.currentTimeMillis() + 365 * 24L * 3600 * 1000);
+ BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
+ X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(x500Principal, serial, notBefore,
+ notAfter, x500Principal, pair.getPublic());
+ ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
+ .setProvider(BC_SECURITY_PROVIDER).build(pair.getPrivate());
+ X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER)
+ .getCertificate(certGen.build(sigGen));
+ cert.checkValidity(new Date());
+ cert.verify(cert.getPublicKey());
+
+ keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
+ return cert;
+ } catch (GeneralSecurityException | OperatorCreationException e) {
+ throw new RuntimeException("Cannot generate self-signed certificate", e);
+ }
+ }
+
+ public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
+ try (PEMParser pemParser = new PEMParser(reader)) {
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BC_SECURITY_PROVIDER);
+ Object object = pemParser.readObject();
+ PrivateKeyInfo privateKeyInfo;
+ if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
+ if (keyPassword == null)
+ throw new IllegalArgumentException("A key password is required");
+ InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword);
+ privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv);
+ } else if (object instanceof PrivateKeyInfo) {
+ privateKeyInfo = (PrivateKeyInfo) object;
+ } else {
+ throw new IllegalArgumentException("Unsupported format for private key");
+ }
+ return converter.getPrivateKey(privateKeyInfo);
+ } catch (IOException | OperatorCreationException | PKCSException e) {
+ throw new RuntimeException("Cannot read private key", e);
+ }
+ }
+
+ public static X509Certificate loadPemCertificate(Reader reader) {
+ try (PEMParser pemParser = new PEMParser(reader)) {
+ X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
+ X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER)
+ .getCertificate(certHolder);
+ return cert;
+ } catch (IOException | CertificateException e) {
+ throw new RuntimeException("Cannot read private key", e);
+ }
+ }
+
+ private static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
+ try {
+ KeyStore store = KeyStore.getInstance(keyStoreType, BC_SECURITY_PROVIDER);
+ if (Files.exists(keyStoreFile)) {
+ try (InputStream fis = Files.newInputStream(keyStoreFile)) {
+ store.load(fis, keyStorePassword);
+ }
+ } else {
+ store.load(null);
+ }
+ return store;
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException("Cannot load keystore " + keyStoreFile, e);
+ }
+ }
+
+ private static void saveKeyStore(Path keyStoreFile, char[] keyStorePassword, KeyStore keyStore) {
+ try {
+ try (OutputStream fis = Files.newOutputStream(keyStoreFile)) {
+ keyStore.store(fis, keyStorePassword);
+ }
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException("Cannot save keystore " + keyStoreFile, e);
+ }
+ }
+
+ /** singleton */
+ private BcUtils() {
+ }
+}
Bundle-Activator: org.argeo.cms.internal.osgi.CmsActivator
Import-Package: \
-com.sun.security.jgss,\
org.osgi.*;version=0.0.0,\
*
package org.argeo.cms;
import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.internal.runtime.KernelConstants;
/** Just a marker interface for the time being. */
public interface CmsSshd {
final static String NODE_USERNAME_ALIAS = "user.name";
- final static String DEFAULT_SSH_HOST_KEY_PATH = KernelConstants.DIR_PRIVATE + '/' + CmsConstants.NODE + ".ser";
+ final static String DEFAULT_SSH_HOST_KEY_PATH = "private/" + CmsConstants.NODE + ".ser";
}
--- /dev/null
+package org.argeo.cms.auth;
+
+import java.io.Console;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Scanner;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+/** Callback handler to be used with a command line UI. */
+public class ConsoleCallbackHandler implements CallbackHandler {
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ Console console = System.console();
+// if (console == null)
+// throw new IllegalStateException("No console available");
+
+ Scanner scanner = null;
+ PrintWriter writer;
+ if (console == null) {// IDE
+ scanner = new Scanner(System.in);
+ writer = new PrintWriter(System.out, true);
+ } else {
+ writer = console.writer();
+
+ }
+ for (int i = 0; i < callbacks.length; i++) {
+ if (callbacks[i] instanceof TextOutputCallback) {
+ TextOutputCallback callback = (TextOutputCallback) callbacks[i];
+ writer.printf(callback.getMessage());
+ } else if (callbacks[i] instanceof NameCallback) {
+ NameCallback callback = (NameCallback) callbacks[i];
+ writer.printf(callback.getPrompt());
+ if (callback.getDefaultName() != null)
+ writer.printf(" (" + callback.getDefaultName() + ")");
+ String answer = console != null ? console.readLine() : scanner.next();
+ if (callback.getDefaultName() != null && answer.trim().equals(""))
+ callback.setName(callback.getDefaultName());
+ else
+ callback.setName(answer);
+ } else if (callbacks[i] instanceof PasswordCallback) {
+ PasswordCallback callback = (PasswordCallback) callbacks[i];
+ writer.printf(callback.getPrompt());
+ char[] answer = console != null ? console.readPassword() : scanner.next().toCharArray();
+ callback.setPassword(answer);
+ Arrays.fill(answer, ' ');
+ }
+// else if (callbacks[i] instanceof LocaleChoice) {
+// LocaleChoice callback = (LocaleChoice) callbacks[i];
+// writer.write("Language");
+// writer.write("\n");
+// for (int j = 0; j < callback.getLocales().size(); j++) {
+// Locale locale = callback.getLocales().get(j);
+// writer.print(j + " : " + locale.getDisplayName() + "\n");
+// }
+// writer.write("(" + callback.getDefaultIndex() + ") : ");
+// String answer = console.readLine();
+// if (answer.trim().equals(""))
+// callback.setSelectedIndex(callback.getDefaultIndex());
+// else
+// callback.setSelectedIndex(new Integer(answer.trim()));
+// }
+ }
+ }
+
+}
package org.argeo.cms.client;
+import java.io.BufferedInputStream;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandler;
import java.net.http.HttpResponse.BodyHandlers;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.Collection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
+import org.argeo.cms.auth.ConsoleCallbackHandler;
import org.argeo.cms.auth.RemoteAuthUtils;
import org.argeo.util.http.HttpHeader;
public class SpnegoHttpClient {
+ public final static String CLIENT_LOGIN_CONTEXT = "CLIENT";
+
public static void main(String[] args) throws MalformedURLException {
// String principal = System.getProperty("javax.security.auth.login.name");
if (args.length == 0) {
URL u = new URL(url);
String server = u.getHost();
- URL jaasUrl = SpnegoHttpClient.class.getResource("jaas.cfg");
+ URL jaasUrl = SpnegoHttpClient.class.getResource("jaas-client-ipa.cfg");
System.setProperty("java.security.auth.login.config", jaasUrl.toExternalForm());
try {
- LoginContext lc = new LoginContext("SINGLE_USER");
+ LoginContext lc = new LoginContext(CLIENT_LOGIN_CONTEXT, new ConsoleCallbackHandler());
lc.login();
HttpClient httpClient = openHttpClient(lc.getSubject());
private static HttpClient openHttpClient(Subject subject) {
HttpClient client = HttpClient.newBuilder() //
-// .sslContext(insecureContext()) //
+ .sslContext(ipaSslContext()) //
.version(HttpClient.Version.HTTP_1_1) //
.build();
return client;
}
- static SSLContext insecureContext() {
- TrustManager[] noopTrustManager = new TrustManager[] { new X509TrustManager() {
- public void checkClientTrusted(X509Certificate[] xcs, String string) {
+ @SuppressWarnings("unchecked")
+ static SSLContext ipaSslContext() {
+ try {
+ final Collection<X509Certificate> certificates;
+ Path caCertificatePath = Paths.get("/etc/ipa/ca.crt");
+ if (Files.exists(caCertificatePath)) {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
+ try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(caCertificatePath))) {
+ certificates = (Collection<X509Certificate>) certificateFactory.generateCertificates(in);
+ }
+ } else {
+ certificates = null;
}
+ TrustManager[] noopTrustManager = new TrustManager[] { new X509TrustManager() {
+ public void checkClientTrusted(X509Certificate[] xcs, String string) {
+ }
- public void checkServerTrusted(X509Certificate[] xcs, String string) {
- }
+ public void checkServerTrusted(X509Certificate[] xcs, String string) {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ if (certificates == null)
+ return null;
+ return certificates.toArray(new X509Certificate[certificates.size()]);
+ }
+ } };
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
- } };
- try {
SSLContext sc = SSLContext.getInstance("ssl");
sc.init(null, noopTrustManager, null);
return sc;
- } catch (KeyManagementException | NoSuchAlgorithmException e) {
- throw new IllegalStateException("Cannot create insecure SSL context ", e);
+ } catch (KeyManagementException | NoSuchAlgorithmException | CertificateException | IOException e) {
+ throw new IllegalStateException("Cannot create SSL context ", e);
}
}
--- /dev/null
+CLIENT {
+ com.sun.security.auth.module.Krb5LoginModule required
+ useTicketCache=true;
+};
+++ /dev/null
-SINGLE_USER {
- com.sun.security.auth.module.Krb5LoginModule required
- useTicketCache=true
- debug=true;
-};
-
-com.sun.security.jgss.krb5.initiate {
- com.sun.security.auth.module.Krb5LoginModule
- required useTicketCache=true;
-};
\ No newline at end of file
+++ /dev/null
-package org.argeo.cms.internal.auth;
-
-import java.io.Console;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Arrays;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-/** Callback handler to be used with a command line UI. */
-public class ConsoleCallbackHandler implements CallbackHandler {
-
- @Override
- public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
- Console console = System.console();
- if (console == null)
- throw new IllegalStateException("No console available");
-
- PrintWriter writer = console.writer();
- for (int i = 0; i < callbacks.length; i++) {
- if (callbacks[i] instanceof TextOutputCallback) {
- TextOutputCallback callback = (TextOutputCallback) callbacks[i];
- writer.write(callback.getMessage());
- } else if (callbacks[i] instanceof NameCallback) {
- NameCallback callback = (NameCallback) callbacks[i];
- writer.write(callback.getPrompt());
- if (callback.getDefaultName() != null)
- writer.write(" (" + callback.getDefaultName() + ")");
- writer.write(" : ");
- String answer = console.readLine();
- if (callback.getDefaultName() != null && answer.trim().equals(""))
- callback.setName(callback.getDefaultName());
- else
- callback.setName(answer);
- } else if (callbacks[i] instanceof PasswordCallback) {
- PasswordCallback callback = (PasswordCallback) callbacks[i];
- writer.write(callback.getPrompt());
- char[] answer = console.readPassword();
- callback.setPassword(answer);
- Arrays.fill(answer, ' ');
- }
-// else if (callbacks[i] instanceof LocaleChoice) {
-// LocaleChoice callback = (LocaleChoice) callbacks[i];
-// writer.write("Language");
-// writer.write("\n");
-// for (int j = 0; j < callback.getLocales().size(); j++) {
-// Locale locale = callback.getLocales().get(j);
-// writer.print(j + " : " + locale.getDisplayName() + "\n");
-// }
-// writer.write("(" + callback.getDefaultIndex() + ") : ");
-// String answer = console.readLine();
-// if (answer.trim().equals(""))
-// callback.setSelectedIndex(callback.getDefaultIndex());
-// else
-// callback.setSelectedIndex(new Integer(answer.trim()));
-// }
- }
- }
-
-}
package org.argeo.cms.internal.runtime;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.Reader;
import java.net.InetAddress;
deployPropertyDefaults.put(CmsDeployProperty.LOCALE, Locale.getDefault().toString());
// certificates
- deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
- deployPropertyDefaults.put(CmsDeployProperty.SSL_PASSWORD, PkiUtils.DEFAULT_KEYSTORE_PASSWORD);
- Path keyStorePath = getDataPath(PkiUtils.DEFAULT_KEYSTORE_PATH);
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORETYPE, KernelConstants.PKCS12);
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_PASSWORD, KernelConstants.DEFAULT_KEYSTORE_PASSWORD);
+ Path keyStorePath = getDataPath(KernelConstants.DEFAULT_KEYSTORE_PATH);
if (keyStorePath != null) {
deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORE, keyStorePath.toAbsolutePath().toString());
}
- Path trustStorePath = getDataPath(PkiUtils.DEFAULT_TRUSTSTORE_PATH);
+ Path trustStorePath = getDataPath(KernelConstants.DEFAULT_TRUSTSTORE_PATH);
if (trustStorePath != null) {
deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORE, trustStorePath.toAbsolutePath().toString());
}
- deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORETYPE, PkiUtils.PKCS12);
- deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD, PkiUtils.DEFAULT_KEYSTORE_PASSWORD);
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORETYPE, KernelConstants.PKCS12);
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD, KernelConstants.DEFAULT_KEYSTORE_PASSWORD);
// SSH
Path authorizedKeysPath = getDataPath(KernelConstants.NODE_SSHD_AUTHORIZED_KEYS_PATH);
private void initCertificates() {
// server certificate
Path keyStorePath = Paths.get(getDeployProperty(CmsDeployProperty.SSL_KEYSTORE));
- Path pemKeyPath = getDataPath(PkiUtils.DEFAULT_PEM_KEY_PATH);
- Path pemCertPath = getDataPath(PkiUtils.DEFAULT_PEM_CERT_PATH);
+ Path pemKeyPath = getDataPath(KernelConstants.DEFAULT_PEM_KEY_PATH);
+ Path pemCertPath = getDataPath(KernelConstants.DEFAULT_PEM_CERT_PATH);
char[] keyStorePassword = getDeployProperty(CmsDeployProperty.SSL_PASSWORD).toCharArray();
// Keystore
KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword,
getDeployProperty(CmsDeployProperty.SSL_KEYSTORETYPE));
try (Reader key = Files.newBufferedReader(pemKeyPath, StandardCharsets.US_ASCII);
- Reader cert = Files.newBufferedReader(pemCertPath, StandardCharsets.US_ASCII);) {
+ BufferedInputStream cert = new BufferedInputStream(Files.newInputStream(pemCertPath));) {
PkiUtils.loadPrivateCertificatePem(keyStore, CmsConstants.NODE, key, keyStorePassword, cert);
Files.createDirectories(keyStorePath.getParent());
PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore);
char[] trustStorePassword = getDeployProperty(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD).toCharArray();
// IPA CA
- Path ipaCaCertPath = Paths.get(PkiUtils.IPA_PEM_CA_CERT_PATH);
+ Path ipaCaCertPath = Paths.get(KernelConstants.IPA_PEM_CA_CERT_PATH);
if (Files.exists(ipaCaCertPath)) {
KeyStore trustStore = PkiUtils.getKeyStore(trustStorePath, trustStorePassword,
getDeployProperty(CmsDeployProperty.SSL_TRUSTSTORETYPE));
- try (Reader cert = Files.newBufferedReader(ipaCaCertPath, StandardCharsets.US_ASCII);) {
+ try (BufferedInputStream cert = new BufferedInputStream(Files.newInputStream(ipaCaCertPath));) {
PkiUtils.loadTrustedCertificatePem(trustStore, trustStorePassword, cert);
Files.createDirectories(keyStorePath.getParent());
PkiUtils.saveKeyStore(trustStorePath, trustStorePassword, trustStore);
}
}
- if (!Files.exists(keyStorePath))
- PkiUtils.createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
-// props.put(JettyHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
-// props.put(JettyHttpConstants.SSL_KEYSTORE, keyStorePath.toString());
-// props.put(JettyHttpConstants.SSL_PASSWORD, new String(keyStorePassword));
-
-// props.put(InternalHttpConstants.SSL_KEYSTORETYPE, "PKCS11");
-// props.put(InternalHttpConstants.SSL_KEYSTORE, "../../nssdb");
-// props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword);
-
+// if (!Files.exists(keyStorePath))
+// PkiUtils.createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
}
public void stop() {
package org.argeo.cms.internal.runtime;
+import org.argeo.api.cms.CmsConstants;
+
/** Internal CMS constants. */
-public interface KernelConstants {
+interface KernelConstants {
// Directories
String DIR_PRIVATE = "private";
// KERBEROS
String DEFAULT_KERBEROS_SERVICE = "HTTP";
+ String DEFAULT_KEYSTORE_PATH = DIR_PRIVATE + '/' + CmsConstants.NODE + ".p12";
+
+ String DEFAULT_TRUSTSTORE_PATH = DIR_PRIVATE + "/trusted.p12";
+
+ String DEFAULT_PEM_KEY_PATH = DIR_PRIVATE + '/' + CmsConstants.NODE + ".key";
+
+ String DEFAULT_PEM_CERT_PATH = DIR_PRIVATE + '/' + CmsConstants.NODE + ".crt";
+
+ String IPA_PEM_CA_CERT_PATH = "/etc/ipa/ca.crt";
+
+ String DEFAULT_KEYSTORE_PASSWORD = "changeit";
+
+ String PKCS12 = "PKCS12";
+
// HTTP client
// String COOKIE_POLICY_BROWSER_COMPATIBILITY = "compatibility";
package org.argeo.cms.internal.runtime;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
-import java.math.BigInteger;
-import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
+import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStore.TrustedCertificateEntry;
import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.cert.Certificate;
import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Date;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+import java.util.Collection;
import java.util.Objects;
-import javax.security.auth.x500.X500Principal;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.InputDecryptorProvider;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
-import org.bouncycastle.pkcs.PKCSException;
-
/**
* Utilities around private keys and certificate, mostly wrapping BouncyCastle
* implementations.
*/
class PkiUtils {
- private final static CmsLog log = CmsLog.getLog(PkiUtils.class);
-
- final static String PKCS12 = "PKCS12";
- final static String JKS = "JKS";
-
- static final String DEFAULT_KEYSTORE_PATH = KernelConstants.DIR_PRIVATE + '/' + CmsConstants.NODE + ".p12";
-
- static final String DEFAULT_TRUSTSTORE_PATH = KernelConstants.DIR_PRIVATE + "/trusted.p12";
-
- static final String DEFAULT_PEM_KEY_PATH = KernelConstants.DIR_PRIVATE + '/' + CmsConstants.NODE + ".key";
-
- static final String DEFAULT_PEM_CERT_PATH = KernelConstants.DIR_PRIVATE + '/' + CmsConstants.NODE + ".crt";
-
- static final String IPA_PEM_CA_CERT_PATH = "/etc/ipa/ca.crt";
-
- static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
-
- private final static String SUN_SECURITY_PROVIDER;
- private final static String SUN_JSSE_SECURITY_PROVIDER;
- private final static String BC_SECURITY_PROVIDER;
- static {
- Security.addProvider(new BouncyCastleProvider());
- // BouncyCastle does not store trusted certificates properly
- // TODO report it
- BC_SECURITY_PROVIDER = "BC";
- SUN_SECURITY_PROVIDER = "SUN";
- SUN_JSSE_SECURITY_PROVIDER = "SunJSSE";
- }
-
- public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
- int keySize, char[] keyPassword) {
- try {
- KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC_SECURITY_PROVIDER);
- kpGen.initialize(keySize, new SecureRandom());
- KeyPair pair = kpGen.generateKeyPair();
- Date notBefore = new Date(System.currentTimeMillis() - 10000);
- Date notAfter = new Date(System.currentTimeMillis() + 365 * 24L * 3600 * 1000);
- BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
- X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(x500Principal, serial, notBefore,
- notAfter, x500Principal, pair.getPublic());
- ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
- .setProvider(BC_SECURITY_PROVIDER).build(pair.getPrivate());
- X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC_SECURITY_PROVIDER)
- .getCertificate(certGen.build(sigGen));
- cert.checkValidity(new Date());
- cert.verify(cert.getPublicKey());
-
- keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
- return cert;
- } catch (GeneralSecurityException | OperatorCreationException e) {
- throw new RuntimeException("Cannot generate self-signed certificate", e);
- }
- }
-
public static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
try {
- KeyStore store = KeyStore.getInstance(keyStoreType, SUN_JSSE_SECURITY_PROVIDER);
+ KeyStore store = KeyStore.getInstance(keyStoreType);
if (Files.exists(keyStoreFile)) {
try (InputStream fis = Files.newInputStream(keyStoreFile)) {
store.load(fis, keyStorePassword);
}
}
-// public static byte[] pemToPKCS12(final String keyFile, final String cerFile, final String password)
-// throws Exception {
-// // Get the private key
-// FileReader reader = new FileReader(keyFile);
-//
-// PEMReader pem = new PemReader(reader, new PasswordFinder() {
-// @Override
-// public char[] getPassword() {
-// return password.toCharArray();
-// }
-// });
-//
-// PrivateKey key = ((KeyPair) pem.readObject()).getPrivate();
-//
-// pem.close();
-// reader.close();
-//
-// // Get the certificate
-// reader = new FileReader(cerFile);
-// pem = new PEMReader(reader);
-//
-// X509Certificate cert = (X509Certificate) pem.readObject();
-//
-// pem.close();
-// reader.close();
-//
-// // Put them into a PKCS12 keystore and write it to a byte[]
-// ByteArrayOutputStream bos = new ByteArrayOutputStream();
-// KeyStore ks = KeyStore.getInstance("PKCS12");
-// ks.load(null);
-// ks.setKeyEntry("alias", (Key) key, password.toCharArray(), new java.security.cert.Certificate[] { cert });
-// ks.store(bos, password.toCharArray());
-// bos.close();
-// return bos.toByteArray();
-// }
-
public static void loadPrivateCertificatePem(KeyStore keyStore, String alias, Reader key, char[] keyPassword,
- Reader cert) {
+ BufferedInputStream cert) {
Objects.requireNonNull(keyStore);
Objects.requireNonNull(key);
try {
}
}
- public static void loadTrustedCertificatePem(KeyStore keyStore,char[] keyStorePassword, Reader cert) {
+ public static void loadTrustedCertificatePem(KeyStore keyStore, char[] keyStorePassword, BufferedInputStream cert) {
try {
X509Certificate certificate = loadPemCertificate(cert);
TrustedCertificateEntry trustedCertificateEntry = new TrustedCertificateEntry(certificate);
}
public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
- try (PEMParser pemParser = new PEMParser(reader)) {
- JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BC_SECURITY_PROVIDER);
- Object object = pemParser.readObject();
- PrivateKeyInfo privateKeyInfo;
- if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
- if (keyPassword == null)
- throw new IllegalArgumentException("A key password is required");
- InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword);
- privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv);
- } else if (object instanceof PrivateKeyInfo) {
- privateKeyInfo = (PrivateKeyInfo) object;
- } else {
- throw new IllegalArgumentException("Unsupported format for private key");
+ try {
+ StringBuilder key = new StringBuilder();
+ try (BufferedReader in = new BufferedReader(reader)) {
+ String line = in.readLine();
+ if (!"-----BEGIN PRIVATE KEY-----".equals(line))
+ throw new IllegalArgumentException("Not a PEM private key");
+ lines: while ((line = in.readLine()) != null) {
+ if ("-----END PRIVATE KEY-----".equals(line))
+ break lines;
+ key.append(line);
+ }
}
- return converter.getPrivateKey(privateKeyInfo);
- } catch (IOException | OperatorCreationException | PKCSException e) {
- throw new RuntimeException("Cannot read private key", e);
- }
- }
- public static X509Certificate loadPemCertificate(Reader reader) {
- try (PEMParser pemParser = new PEMParser(reader)) {
- X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
- X509Certificate cert = new JcaX509CertificateConverter().setProvider(SUN_SECURITY_PROVIDER)
- .getCertificate(certHolder);
- return cert;
- } catch (IOException | CertificateException e) {
- throw new RuntimeException("Cannot read private key", e);
- }
- }
+ byte[] encoded = Base64.getDecoder().decode(key.toString());
- public static void main(String[] args) throws Exception {
- final String ALGORITHM = "RSA";
- final String provider = "BC";
- SecureRandom secureRandom = new SecureRandom();
- long begin = System.currentTimeMillis();
- for (int i = 512; i < 1024; i = i + 2) {
- try {
- KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, provider);
- keyGen.initialize(i, secureRandom);
- keyGen.generateKeyPair();
- } catch (Exception e) {
- System.err.println(i + " : " + e.getMessage());
- }
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
+ return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
+ throw new RuntimeException("Cannot load PEM key", e);
}
- System.out.println((System.currentTimeMillis() - begin) + " ms");
-
- // // String text = "a";
- // String text =
- // "testtesttesttesttesttesttesttesttesttesttesttesttesttesttest";
- // try {
- // System.out.println(text);
- // PrivateKey privateKey;
- // PublicKey publicKey;
- // char[] password = "changeit".toCharArray();
- // String alias = "CN=test";
- // KeyStore keyStore = KeyStore.getInstance("pkcs12");
- // File p12file = new File("test.p12");
- // p12file.delete();
- // if (!p12file.exists()) {
- // keyStore.load(null);
- // generateSelfSignedCertificate(keyStore, new X500Principal(alias),
- // 513, password);
- // try (OutputStream out = new FileOutputStream(p12file)) {
- // keyStore.store(out, password);
- // }
- // }
- // try (InputStream in = new FileInputStream(p12file)) {
- // keyStore.load(in, password);
- // privateKey = (PrivateKey) keyStore.getKey(alias, password);
- // publicKey = keyStore.getCertificateChain(alias)[0].getPublicKey();
- // }
- // // KeyPair key;
- // // final KeyPairGenerator keyGen =
- // // KeyPairGenerator.getInstance(ALGORITHM);
- // // keyGen.initialize(4096, new SecureRandom());
- // // long begin = System.currentTimeMillis();
- // // key = keyGen.generateKeyPair();
- // // System.out.println((System.currentTimeMillis() - begin) + " ms");
- // // keyStore.load(null);
- // // keyStore.setKeyEntry("test", key.getPrivate(), password, null);
- // // try(OutputStream out=new FileOutputStream(p12file)) {
- // // keyStore.store(out, password);
- // // }
- // // privateKey = key.getPrivate();
- // // publicKey = key.getPublic();
- //
- // Cipher encrypt = Cipher.getInstance(ALGORITHM);
- // encrypt.init(Cipher.ENCRYPT_MODE, publicKey);
- // byte[] encrypted = encrypt.doFinal(text.getBytes());
- // String encryptedBase64 =
- // Base64.getEncoder().encodeToString(encrypted);
- // System.out.println(encryptedBase64);
- // byte[] encryptedFromBase64 =
- // Base64.getDecoder().decode(encryptedBase64);
- //
- // Cipher decrypt = Cipher.getInstance(ALGORITHM);
- // decrypt.init(Cipher.DECRYPT_MODE, privateKey);
- // byte[] decrypted = decrypt.doFinal(encryptedFromBase64);
- // System.out.println(new String(decrypted));
- // } catch (Exception e) {
- // e.printStackTrace();
- // }
}
- public static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) {
- // for (Provider provider : Security.getProviders())
- // System.out.println(provider.getName());
- // File keyStoreFile = keyStorePath.toFile();
- char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length);
- if (!Files.exists(keyStorePath)) {
- try {
- Files.createDirectories(keyStorePath.getParent());
- KeyStore keyStore = getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
- generateSelfSignedCertificate(keyStore,
- new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
- 1024, keyPwd);
- saveKeyStore(keyStorePath, keyStorePassword, keyStore);
- if (log.isDebugEnabled())
- log.debug("Created self-signed unsecure keystore " + keyStorePath);
- } catch (Exception e) {
- try {
- if (Files.size(keyStorePath) == 0)
- Files.delete(keyStorePath);
- } catch (IOException e1) {
- // silent
- }
- log.error("Cannot create keystore " + keyStorePath, e);
- }
- } else {
- throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
+ public static X509Certificate loadPemCertificate(BufferedInputStream in) {
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
+ @SuppressWarnings("unchecked")
+ Collection<X509Certificate> certificates = (Collection<X509Certificate>) certificateFactory
+ .generateCertificates(in);
+ if (certificates.isEmpty())
+ throw new IllegalArgumentException("No certificate found");
+ if (certificates.size() != 1)
+ throw new IllegalArgumentException(certificates.size() + " certificates found");
+ return certificates.iterator().next();
+ } catch (CertificateException e) {
+ throw new IllegalStateException("cannot load certifciate", e);
}
}
-
}