no.difi.oxalis.as2.util.SMimeBC Maven / Gradle / Ivy
/*
* Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi)
*
* Licensed under the EUPL, Version 1.1 or – as soon they
* will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
*
* You may not use this work except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
*
* https://joinup.ec.europa.eu/community/eupl/og_page/eupl
*
* Unless required by applicable law or agreed to in
* writing, software distributed under the Licence is
* distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied.
* See the Licence for the specific language governing
* permissions and limitations under the Licence.
*/
package no.difi.oxalis.as2.util;
import no.difi.oxalis.api.lang.OxalisSecurityException;
import no.difi.oxalis.as2.code.Disposition;
import no.difi.oxalis.as2.lang.OxalisAs2InboundException;
import no.difi.oxalis.commons.bouncycastle.BCHelper;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERUTCTime;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.*;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
public class SMimeBC {
private static JcaX509CertificateConverter x509CertificateConverter;
static {
BCHelper.registerProvider();
x509CertificateConverter = new JcaX509CertificateConverter()
.setProvider(BouncyCastleProvider.PROVIDER_NAME);
}
/**
* http://stackoverflow.com/a/31557473/135001
*/
public static X509Certificate verifySignature(Map hashes, byte[] signature)
throws OxalisSecurityException, OxalisAs2InboundException {
try {
CMSSignedData signedData = new CMSSignedData(hashes, signature);
Store store = signedData.getCertificates();
SignerInformationStore signerInformationStore = signedData.getSignerInfos();
for (SignerInformation signerInformation : signerInformationStore.getSigners()) {
Collection certCollection = store.getMatches(signerInformation.getSID());
Iterator certificateIterator = certCollection.iterator();
if (!certificateIterator.hasNext())
throw new OxalisAs2InboundException(Disposition.AUTHENTICATION_FAILED, "Unable to find certificate in signature.", null);
X509CertificateHolder certificateHolder = certificateIterator.next();
X509Certificate certificate = x509CertificateConverter.getCertificate(certificateHolder);
SignerInformationVerifier verifier = getSignerInfoVerifierBuilder().build(certificate);
if (signerInformation.verify(verifier))
return certificate;
}
throw new OxalisSecurityException("Unable to verify signature.");
} catch (CMSSignerDigestMismatchException e) {
throw new OxalisSecurityException("Invalid message digest.", e);
} catch (CMSException | CertificateException | OperatorCreationException e) {
throw new OxalisSecurityException(e.getMessage(), e);
}
}
public static byte[] createSignature(byte[] digest, SMimeDigestMethod digestMethod, PrivateKey privateKey,
X509Certificate certificate) throws OxalisSecurityException {
try {
ASN1EncodableVector signedAttributes = new ASN1EncodableVector();
signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(digestMethod.getOid())));
signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(digest))));
signedAttributes.add(new Attribute(CMSAttributes.signingTime, new DERSet(new DERUTCTime(new Date()))));
AttributeTable signedAttributesTable = new AttributeTable(signedAttributes);
signedAttributesTable.toASN1EncodableVector();
DefaultSignedAttributeTableGenerator signedAttributeGenerator = new DefaultSignedAttributeTableGenerator(signedAttributesTable);
/* Build the SignerInfo generator builder, that will build the generator... that will generate the SignerInformation... */
SignerInfoGeneratorBuilder signerInfoBuilder = new SignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build());
signerInfoBuilder.setSignedAttributeGenerator(signedAttributeGenerator);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
JcaContentSignerBuilder contentSigner = new JcaContentSignerBuilder(digestMethod.getMethod()).setProvider(BouncyCastleProvider.PROVIDER_NAME);
generator.addSignerInfoGenerator(signerInfoBuilder.build(contentSigner.build(privateKey), new X509CertificateHolder(certificate.getEncoded())));
generator.addCertificates(new JcaCertStore(Collections.singletonList(certificate)));
return generator.generate(new CMSAbsentContent()).getEncoded();
} catch (CMSException | IOException | CertificateEncodingException | OperatorCreationException e) {
throw new OxalisSecurityException(e.getMessage(), e);
}
}
private static JcaSimpleSignerInfoVerifierBuilder getSignerInfoVerifierBuilder() {
return new JcaSimpleSignerInfoVerifierBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME);
}
}