/* dCache - http://www.dcache.org/
*
* Copyright (C) 2014-2015 Deutsches Elektronen-Synchrotron
*
* 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 should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.gridsite;
import eu.emi.security.authn.x509.X509Credential;
import eu.emi.security.authn.x509.impl.CertificateUtils;
import eu.emi.security.authn.x509.impl.KeyAndCertCredential;
import eu.emi.security.authn.x509.proxy.ProxyCSRGenerator;
import eu.emi.security.authn.x509.proxy.ProxyCertificateOptions;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.util.stream.Stream;
import org.dcache.delegation.gridsite2.DelegationException;
/**
* An in-progress credential delegation that uses BouncyCastle.
*/
public class BouncyCastleCredentialDelegation implements CredentialDelegation
{
private static final Logger LOG = LoggerFactory.getLogger(BouncyCastleCredentialDelegation.class);
private final DelegationIdentity _id;
private final X509Certificate[] _certificates;
private final String _pemRequest;
protected final KeyPair _keyPair;
BouncyCastleCredentialDelegation(KeyPair keypair, DelegationIdentity id, X509Certificate[] certificates)
throws DelegationException
{
_id = id;
_certificates = certificates;
_keyPair = keypair;
try {
_pemRequest = pemEncode(createRequest(certificates, keypair));
} catch (GeneralSecurityException e) {
LOG.error("Failed to create CSR: {}", e.toString());
throw new DelegationException("cannot create certificate-signing" +
" request: " + e.getMessage());
} catch (IOException e) {
LOG.error("Failed to convert CSR to PEM: {}", e.toString());
throw new DelegationException("cannot PEM-encode certificate-" +
"signing request: " + e.getMessage());
}
}
private static PKCS10CertificationRequest createRequest(X509Certificate[] chain,
KeyPair keyPair) throws GeneralSecurityException
{
ProxyCertificateOptions options = new ProxyCertificateOptions(chain);
options.setPublicKey(keyPair.getPublic());
options.setLimited(true);
return ProxyCSRGenerator.generate(options, keyPair.getPrivate()).getCSR();
}
private static String pemEncode(Object item) throws IOException
{
StringWriter writer = new StringWriter();
try (PEMWriter pem = new PEMWriter(writer)) {
pem.writeObject(item);
}
return writer.toString();
}
@Override
public String getCertificateSigningRequest()
{
return _pemRequest;
}
@Override
public DelegationIdentity getId()
{
return _id;
}
@Override
public X509Credential acceptCertificate(String encodedCertificate) throws DelegationException
{
X509Certificate certificate;
try {
certificate = CertificateUtils.loadCertificate(
new ByteArrayInputStream(encodedCertificate.getBytes(StandardCharsets.UTF_8)),
CertificateUtils.Encoding.PEM);
} catch (IOException e) {
LOG.debug("Bad certificate: {}", e.getMessage());
throw new DelegationException("Supplied certificate is unacceptable: " + e.getMessage());
}
X509Certificate[] newCertificates =
Stream.concat(Stream.of(certificate), Stream.of(_certificates))
.toArray(X509Certificate[]::new);
try {
return new KeyAndCertCredential(_keyPair.getPrivate(), newCertificates);
} catch (KeyStoreException e) {
LOG.error("Failed to create delegated credential: {}", e.getMessage());
throw new DelegationException("Unable to create delegated credential: " + e.getMessage());
}
}
}