/**
* Copyright (C) 2015 Zero11 S.r.l.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.zero11.acme.storage.impl;
import it.zero11.acme.storage.CertificateStorage;
import it.zero11.acme.storage.CertificateStorageException;
import it.zero11.acme.utils.X509Utils;
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.net.URL;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import org.bouncycastle.jce.provider.X509CertParser;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.x509.util.StreamParsingException;
public class DefaultCertificateStorage implements CertificateStorage {
private static final int USER_KEY_SIZE = 4096;
private static final int WEBSITE_KEY_SIZE = 2048;
private final boolean saveCAIntermediateCertificate;
//TODO: handle certificate / csr history and private key renewal as needed
public DefaultCertificateStorage(){
this(false);
}
public DefaultCertificateStorage(boolean saveCAIntermediateCertificate){
this.saveCAIntermediateCertificate = saveCAIntermediateCertificate;
}
@Override
public KeyPair getDomainKeyPair(String[] domains) {
try {
KeyPair pair = getKeyPair(new File(domains[0] + ".key"), WEBSITE_KEY_SIZE);
if (domains.length > 1){
for (String domain:domains){
overrideKeyPairIfDifferent(new File(domain + ".key"), pair);
}
}
return pair;
} catch (NoSuchAlgorithmException|IOException e) {
throw new CertificateStorageException(e);
}
}
@Override
public KeyPair getUserKeyPair() {
try {
return getKeyPair(new File("user.key"), USER_KEY_SIZE);
} catch (NoSuchAlgorithmException|IOException e) {
throw new CertificateStorageException(e);
}
}
private void overrideKeyPairIfDifferent(File filePrivateKey, KeyPair pair) throws IOException {
if (filePrivateKey.exists()){
KeyPair existing;
try(InputStream privateKeyInputStream = new FileInputStream(filePrivateKey)){
existing = X509Utils.loadPEMKeyPair(privateKeyInputStream);
}
if (!existing.equals(pair)){
try(OutputStream outputStream = new FileOutputStream(filePrivateKey)){
X509Utils.savePEM(outputStream, pair);
}
}
}else{
try(OutputStream outputStream = new FileOutputStream(filePrivateKey)){
X509Utils.savePEM(outputStream, pair);
}
}
}
private KeyPair getKeyPair(File filePrivateKey, int size) throws IOException, NoSuchAlgorithmException {
if (filePrivateKey.exists()){
try(InputStream privateKeyInputStream = new FileInputStream(filePrivateKey)){
return X509Utils.loadPEMKeyPair(privateKeyInputStream);
}
}else{
KeyPair newPair = X509Utils.generateKeyPair(size);
try(OutputStream outputStream = new FileOutputStream(filePrivateKey)){
X509Utils.savePEM(outputStream, newPair);
}
return newPair;
}
}
@Override
public void saveCertificate(String[] domains, X509Certificate certificate) {
for (String domain:domains){
try(OutputStream outputStream = new FileOutputStream(domain + ".crt")) {
X509Utils.savePEM(outputStream, certificate);
} catch (IOException e) {
throw new CertificateStorageException(e);
}
}
if (saveCAIntermediateCertificate){
try{
String caIntermediateCertificateURL = X509Utils.getCACertificateURL(certificate);
if (caIntermediateCertificateURL != null){
X509CertificateObject caIntermediateCertificate;
try(InputStream is = new URL(caIntermediateCertificateURL).openStream()){
X509CertParser certParser = new X509CertParser();
certParser.engineInit(is);
caIntermediateCertificate = (X509CertificateObject) certParser.engineRead();
}
for (String domain:domains){
try(OutputStream outputStream = new FileOutputStream(domain + ".chain.crt")) {
X509Utils.savePEM(outputStream, caIntermediateCertificate);
}
}
}
}catch (IOException|StreamParsingException e) {
throw new CertificateStorageException(e);
}
}
}
@Override
public void saveCSR(String[] domains, PKCS10CertificationRequest csr) {
for (String domain:domains){
try(OutputStream outputStream = new FileOutputStream(domain + ".csr")) {
X509Utils.savePEM(outputStream, csr);
} catch (IOException e) {
throw new CertificateStorageException(e);
}
}
}
}