All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.solarnetwork.central.user.pki.dev.DevNodePKIBiz Maven / Gradle / Ivy

/* ==================================================================
 * DevNodePKIBiz.java - Jan 23, 2015 5:31:54 PM
 * 
 * Copyright 2007-2015 SolarNetwork.net Dev Team
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ==================================================================
 */

package net.solarnetwork.central.user.pki.dev;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.UUID;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;
import net.solarnetwork.central.security.SecurityException;
import net.solarnetwork.central.security.SecurityUser;
import net.solarnetwork.central.security.SecurityUtils;
import net.solarnetwork.central.user.biz.NodePKIBiz;
import net.solarnetwork.service.CertificateException;
import net.solarnetwork.service.CertificateService;
import net.solarnetwork.service.CertificationAuthorityService;
import net.solarnetwork.service.ServiceLifecycleObserver;

/**
 * Developer implementation of {@link NodePKIBiz}.
 * 
 * @author matt
 * @version 2.0
 */
public class DevNodePKIBiz implements NodePKIBiz, ServiceLifecycleObserver {

	private static final String WEBSERVER_KEYSTORE_PASSWORD = "dev123";
	private static final String CA_ALIAS = "ca";
	private static final String WEBSERVER_ALIAS = "web";
	private static final String DIR_REQUESTS = "requests";
	private static final String PASSWORD_FILE = "secret";

	private CertificateService certificateService;
	private CertificationAuthorityService caService;
	private File baseDir = new File("var/DeveloperCA");
	private int keySize = 2048;
	private String caDN = "CN=Developer CA, O=SolarDev";

	private final Logger log = LoggerFactory.getLogger(getClass());

	/**
	 * Initialize this service after all properties are configured.
	 * 
	 * 

* This method will generate a new certification authority (CA) certificate * if one does not already exist in the configured {@code baseDir} * directory. When it is generated, a copy of the keystore will be saved as * {@code central.jks} with a password {@code dev123}, which is designed to * be configured with your development webserver to support SolarIn * development. *

* *

* Also, if a new CA certificate is generated, a {@code central-trust.jks} * keystore will be created with a password {@code dev123} that contains * just the CA certificate. This is designed to be configured as the * developer node's trust store, to allow posting to the development SolarIn * service. *

*/ @Override public void serviceDidStartup() { // make sure CA cert created final KeyStore keyStore = loadKeyStore(); X509Certificate caCert = getCertificate(keyStore, CA_ALIAS); if ( caCert == null ) { // generate a new CA caCert = createCACertificate(keyStore, caDN, CA_ALIAS); } InputStream in = null; // create a webserver keystore if one does not exist final String webserverKeystorePassword = WEBSERVER_KEYSTORE_PASSWORD; final File webserverKeyStoreFile = new File(getKeyStoreFile().getParentFile(), "central.jks"); final KeyStore webserverKeyStore; try { if ( webserverKeyStoreFile.canRead() ) { in = new BufferedInputStream(new FileInputStream(webserverKeyStoreFile)); } webserverKeyStore = loadKeyStore(KeyStore.getDefaultType(), in, webserverKeystorePassword); X509Certificate webserverCert = getCertificate(webserverKeyStore, WEBSERVER_ALIAS); if ( webserverCert == null ) { OutputStream out = null; try { // create a new private key + CSR and approve for webserver certificate for developer SolarIn KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048, new SecureRandom()); KeyPair webserverKeyPair = keyGen.generateKeyPair(); webserverCert = certificateService.generateCertificate( "CN=solarnetworkdev.net, O=SolarDev", webserverKeyPair.getPublic(), webserverKeyPair.getPrivate()); String csr = certificateService.generatePKCS10CertificateRequestString(webserverCert, webserverKeyPair.getPrivate()); X509Certificate signedWebserverCert = caService.signCertificate(csr, caCert, getPrivateKey(keyStore, CA_ALIAS)); webserverKeyStore.setKeyEntry(WEBSERVER_ALIAS, webserverKeyPair.getPrivate(), webserverKeystorePassword.toCharArray(), new X509Certificate[] { signedWebserverCert, caCert }); // add the CA cert as a trusted cert webserverKeyStore.setCertificateEntry(CA_ALIAS, caCert); out = new BufferedOutputStream(new FileOutputStream(webserverKeyStoreFile)); webserverKeyStore.store(out, WEBSERVER_KEYSTORE_PASSWORD.toCharArray()); log.info("Development webserver keystore saved to {}; password is dev123", webserverKeyStoreFile.getAbsolutePath()); } catch ( Exception e ) { log.error("Error saving central webserver KeyStore [{}]", webserverKeyStoreFile, e); } finally { if ( out != null ) { try { out.flush(); out.close(); } catch ( IOException e2 ) { log.warn("Error closing central.jks OutputStream", e2); } } } } } catch ( IOException e ) { log.error("Error loading webserver keystore [{}]", webserverKeyStoreFile, e); } finally { if ( in != null ) { try { in.close(); } catch ( IOException e ) { // ignore this } } } // make sure trust CA keystore exists final File trustKeyStoreFile = new File(getKeyStoreFile().getParentFile(), "central-trust.jks"); final KeyStore trustStore; try { if ( trustKeyStoreFile.canRead() ) { in = new BufferedInputStream(new FileInputStream(trustKeyStoreFile)); } else { in = null; } trustStore = loadKeyStore(KeyStore.getDefaultType(), in, WEBSERVER_KEYSTORE_PASSWORD); X509Certificate trustCert = getCertificate(trustStore, CA_ALIAS); if ( trustCert == null ) { OutputStream out = null; try { trustStore.setCertificateEntry(CA_ALIAS, caCert); out = new BufferedOutputStream(new FileOutputStream(trustKeyStoreFile)); trustStore.store(out, WEBSERVER_KEYSTORE_PASSWORD.toCharArray()); log.info("Development node trust keystore saved to {}", trustKeyStoreFile.getAbsolutePath()); } catch ( Exception e ) { log.error("Error saving node trust KeyStore [{}]", trustKeyStoreFile, e); } finally { if ( out != null ) { try { out.flush(); out.close(); } catch ( IOException e2 ) { log.warn("Error closing central.jks OutputStream", e2); } } } } } catch ( IOException e ) { log.error("Error loading trust keystore [{}]", trustKeyStoreFile, e); } finally { if ( in != null ) { try { in.close(); } catch ( IOException e ) { // ignore this } } } } @Override public void serviceDidShutdown() { // nothing to do } @Override public String submitCSR(X509Certificate certificate, PrivateKey privateKey) throws SecurityException { final SecurityUser requestor = SecurityUtils.getCurrentUser(); // mimic Dogtag log.info("Submitting CSR for user {} - {} <{}>", requestor.getUserId(), requestor.getDisplayName(), requestor.getEmail()); final String csr = certificateService.generatePKCS10CertificateRequestString(certificate, privateKey); final String csrID = DigestUtils.md5Hex(csr); final File csrDir = new File(baseDir, DIR_REQUESTS); if ( !csrDir.isDirectory() ) { csrDir.mkdirs(); } final File csrFile = new File(csrDir, csrID); try { FileCopyUtils.copy(csr.getBytes("US-ASCII"), csrFile); } catch ( UnsupportedEncodingException e ) { throw new CertificateException("Error saving CSR: " + e.getMessage()); } catch ( IOException e ) { log.error("Error saving CSR to [{}]", csrFile, e); throw new CertificateException("Error saving CSR data"); } return csrID; } @Override public String submitRenewalRequest(X509Certificate certificate) throws SecurityException { final String csr = certificateService .generatePKCS7CertificateChainString(new X509Certificate[] { certificate }); final String csrID = DigestUtils.md5Hex(csr); final File csrDir = new File(baseDir, DIR_REQUESTS); if ( !csrDir.isDirectory() ) { csrDir.mkdirs(); } final File csrFile = new File(csrDir, csrID); try { FileCopyUtils.copy(csr.getBytes("US-ASCII"), csrFile); } catch ( UnsupportedEncodingException e ) { throw new CertificateException("Error saving CSR: " + e.getMessage()); } catch ( IOException e ) { log.error("Error saving CSR to [{}]", csrFile, e); throw new CertificateException("Error saving CSR data"); } return csrID; } @Override public X509Certificate[] approveCSR(String requestID) { final File csrFile = new File(new File(baseDir, DIR_REQUESTS), requestID); if ( !csrFile.canRead() ) { throw new CertificateException("CSR " + requestID + " not found."); } String csr; try { csr = new String(FileCopyUtils.copyToByteArray(csrFile), "US-ASCII"); } catch ( UnsupportedEncodingException e ) { throw new CertificateException("Error reading CSR: " + e.getMessage()); } catch ( IOException e ) { log.error("Error reading CSR to [{}]", csrFile, e); throw new CertificateException("Error reading CSR data"); } final KeyStore keyStore = loadKeyStore(); X509Certificate caCert = getCertificate(keyStore, CA_ALIAS); if ( caCert == null ) { // generate a new CA caCert = createCACertificate(keyStore, caDN, CA_ALIAS); } PrivateKey caPrivateKey = getPrivateKey(keyStore, CA_ALIAS); X509Certificate signedCert = caService.signCertificate(csr, caCert, caPrivateKey); return new X509Certificate[] { signedCert, caCert }; } @Override public X509Certificate generateCertificate(String dn, PublicKey publicKey, PrivateKey privateKey) throws CertificateException { return certificateService.generateCertificate(dn, publicKey, privateKey); } @Override public String generatePKCS10CertificateRequestString(X509Certificate cert, PrivateKey privateKey) throws CertificateException { return certificateService.generatePKCS10CertificateRequestString(cert, privateKey); } @Override public String generatePKCS7CertificateChainString(X509Certificate[] chain) throws CertificateException { return certificateService.generatePKCS7CertificateChainString(chain); } @Override public X509Certificate[] parsePKCS7CertificateChainString(String pem) throws CertificateException { return certificateService.parsePKCS7CertificateChainString(pem); } private X509Certificate getCertificate(KeyStore keyStore, String alias) { try { return (X509Certificate) keyStore.getCertificate(alias); } catch ( KeyStoreException e ) { throw new CertificateException("Error opening node certificate", e); } } private PrivateKey getPrivateKey(KeyStore keyStore, String alias) { try { return (PrivateKey) keyStore.getKey(alias, getKeyStorePassword().toCharArray()); } catch ( UnrecoverableKeyException e ) { throw new CertificateException("Error opening node certificate", e); } catch ( KeyStoreException e ) { throw new CertificateException("Error opening node certificate", e); } catch ( NoSuchAlgorithmException e ) { throw new CertificateException("Error opening node certificate", e); } } private X509Certificate createCACertificate(KeyStore keyStore, String dn, String alias) { try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(keySize, new SecureRandom()); KeyPair keypair = keyGen.generateKeyPair(); PublicKey publicKey = keypair.getPublic(); PrivateKey privateKey = keypair.getPrivate(); Certificate cert = caService.generateCertificationAuthorityCertificate(dn, publicKey, privateKey); keyStore.setKeyEntry(alias, privateKey, getKeyStorePassword().toCharArray(), new Certificate[] { cert }); saveKeyStore(keyStore); return (X509Certificate) cert; } catch ( NoSuchAlgorithmException e ) { throw new CertificateException("Error setting up node key pair", e); } catch ( KeyStoreException e ) { throw new CertificateException("Error setting up node key pair", e); } } private String getKeyStorePassword() { File pwFile = new File(baseDir, PASSWORD_FILE); if ( pwFile.canRead() ) { try { return new String(FileCopyUtils.copyToByteArray(pwFile), "US-ASCII"); } catch ( UnsupportedEncodingException e ) { throw new CertificateException( "Error decoding keystore secret file " + pwFile.getAbsolutePath(), e); } catch ( IOException e ) { throw new CertificateException( "Error reading keystore secret file" + pwFile.getAbsolutePath(), e); } } // generate new random password String pw = UUID.randomUUID().toString(); if ( !baseDir.isDirectory() ) { baseDir.mkdirs(); } try { FileCopyUtils.copy(pw.getBytes(), pwFile); } catch ( IOException e ) { throw new CertificateException( "Unable to save keystore secret file " + pwFile.getAbsolutePath(), e); } return pw; } private File getKeyStoreFile() { return new File(baseDir, "ca.jks"); } private synchronized KeyStore loadKeyStore() { File ksFile = getKeyStoreFile(); InputStream in = null; String passwd = getKeyStorePassword(); try { if ( ksFile.isFile() ) { in = new BufferedInputStream(new FileInputStream(ksFile)); } return loadKeyStore(KeyStore.getDefaultType(), in, passwd); } catch ( IOException e ) { throw new CertificateException("Error opening file " + ksFile.getAbsolutePath(), e); } } private KeyStore loadKeyStore(String type, InputStream in, String password) { if ( password == null ) { password = ""; } KeyStore keyStore = null; try { keyStore = KeyStore.getInstance(type); keyStore.load(in, password.toCharArray()); return keyStore; } catch ( GeneralSecurityException e ) { throw new CertificateException("Error loading certificate key store", e); } catch ( IOException e ) { String msg; if ( e.getCause() instanceof UnrecoverableKeyException ) { msg = "Invalid password loading key store"; } else { msg = "Error loading certificate key store"; } throw new CertificateException(msg, e); } finally { if ( in != null ) { try { in.close(); } catch ( IOException e ) { // ignore this one } } } } private synchronized void saveKeyStore(KeyStore keyStore) { if ( keyStore == null ) { return; } final File ksFile = getKeyStoreFile(); final File ksDir = ksFile.getParentFile(); if ( !ksDir.isDirectory() && !ksDir.mkdirs() ) { throw new RuntimeException("Unable to create KeyStore directory: " + ksFile.getParent()); } OutputStream out = null; try { String passwd = getKeyStorePassword(); out = new BufferedOutputStream(new FileOutputStream(ksFile)); keyStore.store(out, passwd.toCharArray()); } catch ( KeyStoreException e ) { throw new CertificateException("Error saving certificate key store", e); } catch ( NoSuchAlgorithmException e ) { throw new CertificateException("Error saving certificate key store", e); } catch ( java.security.cert.CertificateException e ) { throw new CertificateException("Error saving certificate key store", e); } catch ( IOException e ) { throw new CertificateException("Error saving certificate key store", e); } finally { if ( out != null ) { try { out.flush(); out.close(); } catch ( IOException e ) { throw new CertificateException("Error closing KeyStore file: " + ksFile.getPath(), e); } } } } public void setCertificateService(CertificateService certificateService) { this.certificateService = certificateService; } public void setBaseDir(File baseDir) { this.baseDir = baseDir; } public File getBaseDir() { return baseDir; } public void setCaService(CertificationAuthorityService caService) { this.caService = caService; } public void setKeySize(int keySize) { this.keySize = keySize; } public void setCaDN(String caDN) { this.caDN = caDN; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy