SSLCertFactory.java
/*
** Module : SSLCertFactory.java
** Abstract : defines APIs to generate SSL certificates and their private keys.
**
** Copyright (c) 2014-2017, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description----------------------------------
** 001 CA 20140207 First version.
** 002 CA 20160106 Enforce a RSA key length of at least 2048 bits.
** 003 CA 20170630 Added setRootCA().
** 004 SBI 20171030 Added loadRootCA(certPemFile, privateKeyPemFile, certChainPemFile) to load
* external public and private key pair and its trusted certificate chain.
** 005 SBI 20171111 Added getFullChainFromRoot and setCertificateChain.
** 006 SBI 20171112 Added CertificateSuite, loadCertificateSuite(..).
** SBI 20171116 Added getFullChain() to CertificateSuite.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.security;
import java.math.*;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.util.*;
/**
* Define APIs to generate SSL certificates and their private keys.
* <p>
* Before starting to generate certificates, a root CA needs to be generated. This will be used
* for signing all the issued certificates.
*/
public abstract class SSLCertFactory
{
/** The minimum length (in bits) of a RSA key. */
public static final int MIN_RSA_KEY_STRENGTH = 2048;
/** The public key exponent. */
protected BigInteger exponent;
/** The private key size: {@link #MIN_RSA_KEY_STRENGTH} bits or better.*/
protected int keyStrength;
/**
* Decrypt a private key which was previously encrypted with the given password.
*
* @param encrypted
* The bytes representing the encrypted private key.
* @param password
* The encryption password.
*
* @return The decrypted {@link PrivateKey private key}.
*
* @throws SSLCertGenException
* If the private key could not be decrypted.
*/
public abstract PrivateKey decryptPrivateKey(byte[] encrypted, String password)
throws SSLCertGenException;
/**
* Encrypt the given key using the provided password.
*
* @param key
* The key to be encrypted.
* @param password
* The encryption password.
*
* @return A byte array with the encrypted key.
*
* @throws SSLCertGenException
* If the key could not be encrypted.
*/
public abstract byte[] encryptPrivateKey(Key key, String password)
throws SSLCertGenException;
/**
* Generate a certificate and sign it with the already generated root CA.
* <p>
* The encrypted private key will be saved in the specified {@code certKeyStore}; the encrypt
* password will be returned by this API.
* <p>
* The public certificate will be saved in the specified {@code certStore}.
*
* @param alias
* The certificate alias, used to store the private key and certificate.
* @param validity
* The certificate validity, in years.
* @param commonName
* The certificate's common name (CN).
* @param fieldMap
* A map with additional subject attributes.
* @param certStore
* The store where to save the certificate.
* @param certKeyStore
* The store where to save the private key.
*
* @return The password used to encrypt the private key in the store.
*
* @throws SSLCertGenException
* If the root CA is not yet generated or the certificate could not be generated.
*/
public abstract String generateCertificate(String alias,
int validity,
String commonName,
Map<String, String> fieldMap,
KeyStore certStore,
KeyStore certKeyStore)
throws SSLCertGenException;
/**
* Set the details for the root CA certificate.
*
* @param cert
* The certificate.
* @param pk
* The private key.
*/
public abstract void setRootCA(X509Certificate cert, PrivateKey pk)
throws SSLCertGenException;
/**
* Generate a root CA certificate, which will be used to sign all the issues certificates.
* <p>
* The encrypted private key will be saved in the specified {@code certKeyStore}; the encrypt
* password will be returned by this API.
* <p>
* The public root CA certificate will be saved in the specified {@code certStore}.
*
* @param alias
* The certificate alias, used to store the private key and certificate.
* @param validity
* The certificate validity, in years.
* @param commonName
* The certificate's common name (CN).
* @param fieldMap
* A map with additional subject attributes.
* @param certStore
* The store where to save the certificate.
* @param certKeyStore
* The store where to save the private key.
*
* @return The password used to encrypt the private key in the store.
*
* @throws SSLCertGenException
* If the root CA is not yet generated or the certificate could not be generated.
*/
public abstract String generateRootCA(String alias,
int validity,
String commonName,
Map<String, String> fieldMap,
KeyStore certStore,
KeyStore certKeyStore)
throws SSLCertGenException;
/**
* Generate a self-signed certificate. This may be used as a root CA.
* <p>
* The encrypted private key will be saved in the specified {@code certKeyStore}; the encrypt
* password will be returned by this API.
* <p>
* The public certificate will be saved in the specified {@code certStore}.
*
* @param alias
* The certificate alias, used to store the private key and certificate.
* @param certificateAuthority
* Flag indicating if the generated self-signed certificate will be used as the root
* CA.
* @param validity
* The certificate validity, in years.
* @param commonName
* The certificate's common name (CN).
* @param fieldMap
* A map with additional subject attributes.
* @param certStore
* The store where to save the certificate.
* @param certKeyStore
* The store where to save the private key.
*
* @return The password used to encrypt the private key in the store.
*
* @throws SSLCertGenException
* If the root CA is not yet generated or the certificate could not be generated.
*/
public abstract String generateSelfSignedCertificate(String alias,
boolean certificateAuthority,
int validity,
String commonName,
Map<String, String> fieldMap,
KeyStore certStore,
KeyStore certKeyStore)
throws SSLCertGenException;
/**
* Loads externally generated certificate suite of public and private certificates with
* trusted certificate chain if this PEM file is provided.
*
* @param certPemFile
* The PEM file that contains the public certificate signed by well-known authorities
* or self-signed.
* @param privateKeyPemFile
* The PEM file that contains the private certificate signed by well-known authorities
* or self-signed.
* @param certChainPemFile
* The PEM file that contains the intermediate certificates up to the well-known
* authority CA certificate.
*
* @return The certificate suite
*
* @throws SSLCertGenException
* If IO or parse exceptions are thrown
*/
public abstract CertificateSuite loadCertificateSuite(String certPemFile,
String privateKeyPemFile,
String certChainPemFile)
throws SSLCertGenException;
/**
* Save the root CA into the provided certificate and private key stores to be accessed by the
* given alias.
*
* @param alias
* The certificate alias
* @param certStore
* The certificate store
* @param certKeyStore
* The certificate private key store
*
* @return The protected private key password.
*
* @throws SSLCertGenException
*
*/
public abstract String saveRootCA(String alias,
KeyStore certStore,
KeyStore certKeyStore)
throws SSLCertGenException;
/**
* Get the map with the mandatory subject attributes. The key is the string representation of
* this attribute, while the value will be the associated description.
*
* @return See above.
*/
public abstract Map<String, String> getMandatorySubjectFields();
/**
* Initialize this SSL certificate factory.
*
* @param keyStrength
* The private key size: {@link SSLCertFactory#MIN_RSA_KEY_STRENGTH} bits or better.
* If {@code null}, defaults to {@link SSLCertFactory#MIN_RSA_KEY_STRENGTH}.
* @param exponent
* The public key exponent. If {@code null}, defaults to 65537.
* WARNING: a wrong value may result in vulnerable SSL private keys and also 3rd party
* software might not accept them. Use with care.
*
* @throws SSLCertGenException
* If the factory could not be instantiated.
*/
public void init(Integer keyStrength, BigInteger exponent)
throws SSLCertGenException
{
this.keyStrength = (keyStrength == null) ? MIN_RSA_KEY_STRENGTH : keyStrength;
if (this.keyStrength < MIN_RSA_KEY_STRENGTH)
{
final String msg = "The RSA private key size %d is smaller than %d bits.";
throw new SSLCertGenException(String.format(msg, this.keyStrength, MIN_RSA_KEY_STRENGTH));
}
this.exponent = (exponent == null) ? new BigInteger("65537") : exponent;
if (this.exponent.testBit(0) == false)
{
final String msg = "The RSA public exponent %s is even.";
throw new SSLCertGenException(String.format(msg, this.exponent.toString()));
}
}
/**
* Build a full chain from the root certificate up to the well-known authority.
*
* @return Certificate chain
*/
public abstract Certificate[] getFullChainFromRoot();
/**
* Sets the trusted certificate chain.
*
* @param chain
* The root certificate chain up to the well-known authority
*/
public abstract void setCertificateChain(Certificate[] chain);
/**
* Holds public and private certificates with trusted certificate chain up to a well-known CA.
*/
public static class CertificateSuite
{
/** Private certificate */
public PrivateKey privateKey;
/** Public certificate */
public X509Certificate publicKey;
/** Certificate chain */
public X509Certificate[] chain;
/**
* Returns its full chain.
*
* @return The full chain
*/
public X509Certificate[] getFullChain()
{
X509Certificate[] fullChain = new X509Certificate[chain.length + 1];
fullChain[0] = publicKey;
if (chain != null && chain.length > 0)
{
System.arraycopy(chain, 0, fullChain, 1, chain.length);
}
return fullChain;
}
}
}