Signatures.java
/*-
* #%L
* io.earcam.utilitarian.security
* %%
* Copyright (C) 2017 earcam
* %%
* SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
*
* You <b>must</b> choose to accept, in full - any individual or combination of
* the following licenses:
* <ul>
* <li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
* <li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
* <li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
* <li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
* </ul>
* #L%
*/
package io.earcam.utilitarian.security;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSSignedDataParser;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OperatorException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import io.earcam.unexceptional.Exceptional;
import io.earcam.unexceptional.UncheckedSecurityException;
public final class Signatures {
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
private Signatures()
{}
public static byte[] sign(byte[] contents, OpenedKeyStore keyStore, String signatureAlgorithm)
{
try {
CMSSignedDataGenerator gen = createSignedDataGenerator(keyStore, signatureAlgorithm);
CMSTypedData cmsData = new CMSProcessableByteArray(contents);
CMSSignedData signedData = gen.generate(cmsData);
return signedData.getEncoded();
} catch(CMSException | OperatorException | IllegalArgumentException | IOException | GeneralSecurityException e) {
throw new UncheckedSecurityException(new GeneralSecurityException(e));
}
}
private static CMSSignedDataGenerator createSignedDataGenerator(OpenedKeyStore openedKeyStore, String signatureAlgorithm)
throws GeneralSecurityException, OperatorException, CMSException
{
List<Certificate> certChain = Arrays.asList(openedKeyStore.getCertificateChain());
JcaCertStore certStore = new JcaCertStore(certChain);
Certificate cert = openedKeyStore.getCertificate();
ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).setProvider(PROVIDER).build(openedKeyStore.privateKey());
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider(PROVIDER).build();
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
generator.addSignerInfoGenerator(sig);
generator.addCertificates(certStore);
return generator;
}
public static List<X509Certificate> certificatesFromSignature(byte[] encapSigData)
{
try {
CMSSignedDataParser parser = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider(PROVIDER).build(), encapSigData);
@SuppressWarnings("unchecked")
Store<X509CertificateHolder> certStore = parser.getCertificates();
SignerInformationStore signers = parser.getSignerInfos();
List<X509Certificate> certificates = new ArrayList<>();
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(PROVIDER);
for(SignerInformation signer : signers.getSigners()) {
@SuppressWarnings("unchecked")
Collection<X509CertificateHolder> holders = certStore.getMatches(signer.getSID());
for(X509CertificateHolder holder : holders) {
X509Certificate certificate = Exceptional.apply(converter::getCertificate, holder);
certificates.add(certificate);
}
}
return certificates;
} catch(CMSException | OperatorCreationException e) {
throw new UncheckedSecurityException(new GeneralSecurityException(e));
}
}
}