package com.juick.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.Base64Utils; import sun.security.x509.*; import javax.annotation.PostConstruct; import javax.net.ssl.KeyManagerFactory; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; @Component public class KeystoreManager { private static final Logger logger = LoggerFactory.getLogger("com.juick.server"); @Value("${keystore:juick.p12}") private String keystore; @Value("${keystore_password:secret}") private String keystorePassword; private KeyStore ks; private KeyManagerFactory kmf; @PostConstruct public void init() throws GeneralSecurityException, IOException { try (InputStream ksIs = new FileInputStream(keystore)) { ks = KeyStore.getInstance("PKCS12"); ks.load(ksIs, keystorePassword.toCharArray()); kmf = KeyManagerFactory.getInstance(KeyManagerFactory .getDefaultAlgorithm()); kmf.init(ks, keystorePassword.toCharArray()); } catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException e) { logger.warn("Keystore error, creating self-signed", e); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(4096); KeyPair keyPair = keyPairGenerator.generateKeyPair(); Certificate[] chain = {generateCertificate("cn=localhost", keyPair, 365, "SHA256withRSA")}; ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(null, null); ks.setKeyEntry("1", keyPair.getPrivate(), keystorePassword.toCharArray(), chain); kmf = KeyManagerFactory.getInstance(KeyManagerFactory .getDefaultAlgorithm()); kmf.init(ks, keystorePassword.toCharArray()); } } public KeyStore getKeystore() { return ks; } public KeyManagerFactory getKeymanagerFactory() { return kmf; } private KeyPair getKeyPair() { Key privateKey = null; try { privateKey = ks.getKey("1", keystorePassword.toCharArray()); Certificate certificate = ks.getCertificate("1"); return new KeyPair(certificate.getPublicKey(), (PrivateKey) privateKey); } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { e.printStackTrace(); } return null; } public String getPublicKey() { return String.format("-----BEGIN RSA PUBLIC KEY-----\n%s\n-----END RSA PUBLIC KEY-----\n", new String(Base64Utils.encode(getKeyPair().getPublic().getEncoded()))); } private X509Certificate generateCertificate(String dn, KeyPair keyPair, int validity, String sigAlgName) throws GeneralSecurityException, IOException { PrivateKey privateKey = keyPair.getPrivate(); X509CertInfo info = new X509CertInfo(); Date from = new Date(); Date to = new Date(from.getTime() + validity * 1000L * 24L * 60L * 60L); CertificateValidity interval = new CertificateValidity(from, to); BigInteger serialNumber = new BigInteger(64, new SecureRandom()); X500Name owner = new X500Name(dn); AlgorithmId sigAlgId = new AlgorithmId(AlgorithmId.sha256WithRSAEncryption_oid); info.set(X509CertInfo.VALIDITY, interval); info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber)); info.set(X509CertInfo.SUBJECT, owner); info.set(X509CertInfo.ISSUER, owner); info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic())); info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(sigAlgId)); // Sign the cert to identify the algorithm that's used. X509CertImpl certificate = new X509CertImpl(info); certificate.sign(privateKey, sigAlgName); // Update the algorith, and resign. sigAlgId = (AlgorithmId) certificate.get(X509CertImpl.SIG_ALG); info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgId); certificate = new X509CertImpl(info); certificate.sign(privateKey, sigAlgName); return certificate; } }