WebCertificates.java
/*
** Module : WebCertificates.java
** Abstract : Defines web certificates configuration manager.
**
** Copyright (c) 2017, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description----------------------------------
** 001 SBI 20171116 Added webCertificates configuration to the directory.
*/
/*
** 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.io.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import com.goldencode.p2j.cfg.*;
import com.goldencode.p2j.directory.*;
import com.goldencode.p2j.security.SSLCertFactory.*;
/**
* Defines web certificates configuration manager.
*/
public class WebCertificates
{
/** The root node for certificates */
private static final String CERTS_ROOT = "/security/certificates";
/** The root node for web certificates */
private static final String WEB_ROOT_CERTS = "/security/certificates/webCertificates";
/** Certificates builder */
private final SSLCertFactory factory;
/** X.509 certificate factory */
private final CertificateFactory cf;
/**
* The default constructor
*
* @throws SSLCertGenException
* If BCCertFactory can't be instantiated.
*/
public WebCertificates()
throws SSLCertGenException
{
this(new BCCertFactory());
}
/**
* Creates parameterized web certificates reader.
*
* @param factory
* The SSL certificates utility
*
* @throws SSLCertGenException
* If CertificateFactory can't be instantiated.
*/
public WebCertificates(SSLCertFactory factory)
throws SSLCertGenException
{
this.factory = factory;
try
{
this.cf = CertificateFactory.getInstance("X.509");
}
catch (CertificateException ex)
{
throw new SSLCertGenException(ex);
}
}
/**
* Reads web alias for the given server.
*
* @param ds
* The directory service
* @param serverId
* The server identifier
*
* @return The web alias
*/
public String getWebAlias(DirectoryService ds, String serverId)
{
StringBuilder nodePath = new StringBuilder(WEB_ROOT_CERTS);
nodePath.append("/").append(serverId).append("/alias");
return ds.getNodeString(nodePath.toString(), "value");
}
/**
* Reads the password that is used to encrypt the web private certificate for the given server.
*
* @param ds
* The directory service
* @param serverId
* The server identifier
*
* @return The private key password
*/
public String getPrivateKeyPassword(DirectoryService ds, String serverId)
{
StringBuilder nodePath = new StringBuilder(WEB_ROOT_CERTS);
nodePath.append("/").append(serverId).append("/key/key-password");
byte[] passwordData = ds.getNodeByteArray(nodePath.toString(), "value");
if (passwordData == null || passwordData.length == 0)
{
return null;
}
return new String(passwordData);
}
/**
* Reads web certificates from the directory for the given server.
*
* @param ds
* The directory service
* @param serverId
* The server identifier
*
* @return The certificate suite
*
* @throws SSLCertGenException
* If web certificates can't be read due to the current settings
*/
public CertificateSuite getWebCertificates(DirectoryService ds, String serverId)
throws SSLCertGenException
{
try
{
StringBuilder nodePath = new StringBuilder(WEB_ROOT_CERTS);
nodePath.append("/").append(serverId);
String webCertRootPath = nodePath.toString();
nodePath.append("/alias");
String webAlias = ds.getNodeString(nodePath.toString(), "value");
if (webAlias == null || webAlias.trim().isEmpty())
{
throw new ConfigurationException("Missed web alias - " + nodePath);
}
nodePath.setLength(0);
nodePath.append(webCertRootPath).append("/certificate");
byte[] data = ds.getNodeByteArray(nodePath.toString(), "value");
if (data == null || data.length == 0)
{
throw new ConfigurationException("Missed certificate - " + nodePath);
}
ByteArrayInputStream bis = new ByteArrayInputStream(data);
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
List<X509Certificate> chainList = new LinkedList<X509Certificate>();
nodePath.setLength(0);
nodePath.append(webCertRootPath).append("/chain");
String[] nodes = ds.enumerateNodes(nodePath.toString());
if (nodes == null || nodes.length == 0)
{
throw new ConfigurationException("Missed certificates chain - " + nodePath);
}
for(String node : nodes)
{
String fullId = nodePath.toString() + "/" + node;
data = ds.getNodeByteArray(fullId, "value");
if (data == null || data.length == 0)
{
throw new ConfigurationException("Missed certificate - " + fullId);
}
bis = new ByteArrayInputStream(data);
chainList.add((X509Certificate)cf.generateCertificate(bis));
}
nodePath.setLength(0);
nodePath.append(webCertRootPath).append("/key");
if (ds.getNodeClass(nodePath.toString()) == null)
{
throw new ConfigurationException("Missed private key - " + nodePath);
}
String kpId = nodePath.toString() + "/key-password";
String keId = nodePath.toString() + "/key-entry";
byte[] kpData = ds.getNodeByteArray(kpId, "value");
if (kpData == null)
{
throw new ConfigurationException("Missed private key password - " + kpId);
}
String keyEntryPassword = new String(kpData);
byte[] encrypted = ds.getNodeByteArray(keId, "value");
if (encrypted == null)
{
throw new ConfigurationException("Missed private key encrypted data - " + keId);
}
PrivateKey pkey = factory.decryptPrivateKey(encrypted, keyEntryPassword);
CertificateSuite suite = new CertificateSuite();
suite.privateKey = pkey;
suite.publicKey = cert;
suite.chain = chainList.toArray(new X509Certificate[chainList.size()]);
return suite;
}
catch(Throwable ex)
{
throw new SSLCertGenException("webCertificates is corrupted!", ex);
}
}
/**
* Writes the given certificate suite into the directory located by
* /security/certificates/webCertificates/serverId.
*
* @param suite
* The given certificate suite
* @param ds
* The directory service
* @param serverId
* The server identifier
* @param alias
* The web certificate alias
* @param keyPassword
* The private key password
*
* @throws SSLCertGenException
* If it is failed.
*/
public void setCertificateSuite(CertificateSuite suite,
DirectoryService ds,
String serverId,
String alias,
String keyPassword)
throws SSLCertGenException
{
try
{
StringBuilder nodePath = new StringBuilder(WEB_ROOT_CERTS);
if (ds.getNodeClass(nodePath.toString()) == null)
{
NodeHelper.openBatch(ds, CERTS_ROOT);
NodeHelper.addNode(ds, nodePath.toString(), "container", (Attribute[]) null);
NodeHelper.closeBatch(ds, CERTS_ROOT);
}
NodeHelper.openBatch(ds, WEB_ROOT_CERTS);
nodePath.append("/").append(serverId);
String webCertRootPath = nodePath.toString();
if (ds.getNodeClass(webCertRootPath) == null)
{
NodeHelper.addNode(ds, webCertRootPath, "container", (Attribute[]) null);
}
nodePath.append("/alias");
NodeHelper.deleteNode(ds, nodePath.toString());
NodeHelper.addNode(ds, nodePath.toString(), "string", alias);
nodePath.setLength(0);
nodePath.append(webCertRootPath);
nodePath.append("/certificate");
NodeHelper.deleteNode(ds, nodePath.toString());
NodeHelper.addNode(ds, nodePath.toString(), "bytes", suite.publicKey.getEncoded());
nodePath.setLength(0);
nodePath.append(webCertRootPath);
nodePath.append("/chain");
NodeHelper.deleteNode(ds, nodePath.toString());
NodeHelper.addNode(ds, nodePath.toString(), "container", (Attribute[]) null);
X509Certificate[] chain = suite.chain;
StringBuilder aliasBuffer = new StringBuilder(alias);
for(int i = 0; i < chain.length; i++)
{
aliasBuffer.append("-").append(i + 1);
String chainNodeId = nodePath.toString() + "/" + aliasBuffer.toString();
NodeHelper.addNode(ds, chainNodeId, "bytes", chain[i].getEncoded());
}
nodePath.setLength(0);
nodePath.append(webCertRootPath);
nodePath.append("/key");
NodeHelper.deleteNode(ds, nodePath.toString());
NodeHelper.addNode(ds, nodePath.toString(), "container", (Attribute[]) null);
NodeHelper.addNode(ds, nodePath.toString() + "/key-password", "bytes", keyPassword.getBytes());
byte[] encoded = factory.encryptPrivateKey(suite.privateKey, keyPassword);
NodeHelper.addNode(ds, nodePath.toString() + "/key-entry", "bytes", encoded);
NodeHelper.closeBatch(ds, WEB_ROOT_CERTS);
}
catch (Throwable ex)
{
throw new SSLCertGenException("writeCertificateSuite is failed", ex);
}
}
}