/*
* DSS - Digital Signature Services
*
* Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
*
* Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* DSS 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>.
*/
package eu.europa.ec.markt.dss;
import java.io.IOException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.Date;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1UTCTime;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
import org.bouncycastle.asn1.ocsp.OCSPResponse;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.signature.DSSDocument;
/**
* Utility class that contains some XML related method.
*
* @version $Revision: 2221 $ - $Date: 2013-06-11 11:53:27 +0200 (Tue, 11 Jun 2013) $
*/
public final class DSSASN1Utils {
private static final Logger LOG = LoggerFactory.getLogger(DSSASN1Utils.class);
/**
* This class is an utility class and cannot be instantiated.
*/
private DSSASN1Utils() {
}
/**
* This method returns {@code T extends ASN1Primitive} created from array of bytes. The {@code IOException} is transformed in {@code DSSException}.
*
* @param bytes array of bytes to be transformed to {@code ASN1Primitive}
* @return new {@code T extends ASN1Primitive}
*/
public static <T extends ASN1Primitive> T toASN1Primitive(final byte[] bytes) throws DSSException {
try {
@SuppressWarnings("unchecked") final T asn1Primitive = (T) ASN1Primitive.fromByteArray(bytes);
return asn1Primitive;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method checks if a given {@code DEROctetString} is null.
*
* @param derOctetString
* @return
*/
public static boolean isDEROctetStringNull(final DEROctetString derOctetString) {
final byte[] derOctetStringBytes = derOctetString.getOctets();
final ASN1Primitive asn1Null = DSSASN1Utils.toASN1Primitive(derOctetStringBytes);
return DERNull.INSTANCE.equals(asn1Null);
}
/**
* This method return DER encoded ASN1 attribute. The {@code IOException} is transformed in {@code DSSException}.
*
* @param asn1Encodable asn1Encodable to be DER encoded
* @return array of bytes representing the DER encoded asn1Encodable
*/
public static byte[] getDEREncoded(ASN1Encodable asn1Encodable) {
try {
return asn1Encodable.toASN1Primitive().getEncoded(ASN1Encoding.DER);
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method return {@code X509Certificate} representing {@code X509CertificateHolder}. The {@code CertificateParsingException} is transformed in {@code
* DSSException}.
*
* @param certificateHolder {@code X509CertificateHolder}
* @return {@code X509Certificate}.
* @throws DSSException
*/
public static X509Certificate getCertificate(final X509CertificateHolder certificateHolder) throws DSSException {
try {
final X509Certificate certificate = new X509CertificateObject(certificateHolder.toASN1Structure());
return certificate;
} catch (CertificateParsingException e) {
throw new DSSException(e);
}
}
/**
* This method returns DER encoded array of bytes representing {@code X509Certificate} for given {@code X509CertificateHolder}. The {@code
* IOException} is transformed in {@code DSSException}.
*
* @param certificateHolder {@code X509CertificateHolder}
* @return DER encoded array of bytes representing {@code X509Certificate}.
* @throws DSSException
*/
public static byte[] getCertificateDEREncoded(final X509CertificateHolder certificateHolder) throws DSSException {
try {
final byte[] bytes = certificateHolder.getEncoded();
return bytes;
} catch (IOException e) {
throw new DSSException(e);
}
}
public static byte[] getEncoded(final AlgorithmIdentifier algorithmIdentifier) throws DSSException {
try {
return algorithmIdentifier.getEncoded(ASN1Encoding.DER);
} catch (IOException e) {
throw new DSSException(e);
}
}
public static byte[] getEncoded(final ASN1Sequence signPolicyInfo) throws DSSException {
try {
return signPolicyInfo.getEncoded(ASN1Encoding.DER);
} catch (IOException e) {
throw new DSSException(e);
}
}
public static Date toDate(final ASN1UTCTime attrValue) throws DSSException {
try {
return attrValue.getDate();
} catch (ParseException e) {
throw new DSSException(e);
}
}
public static Date toDate(final ASN1GeneralizedTime notBeforeTime) throws DSSException {
try {
return notBeforeTime.getDate();
} catch (ParseException e) {
throw new DSSException(e);
}
}
public static String toString(final ASN1OctetString value) {
return new String(value.getOctets());
}
/**
* Returns the ASN.1 encoded representation of {@code CMSSignedData}.
*
* @param data
* @return
* @throws DSSException
*/
public static byte[] getEncoded(final CMSSignedData data) throws DSSException {
try {
return data.getEncoded();
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method generate {@code CMSSignedData} using the provided #{@code CMSSignedDataGenerator}, the content and the indication if the content should be encapsulated.
*
* @param generator
* @param content
* @param encapsulate
* @return
* @throws DSSException
*/
public static CMSSignedData generateCMSSignedData(final CMSSignedDataGenerator generator, final CMSProcessableByteArray content,
final boolean encapsulate) throws DSSException {
try {
final CMSSignedData cmsSignedData = generator.generate(content, encapsulate);
return cmsSignedData;
} catch (CMSException e) {
throw new DSSException(e);
}
}
/**
* Returns an ASN.1 encoded bytes representing the {@code TimeStampToken}
*
* @param timeStampToken {@code TimeStampToken}
* @return Returns an ASN.1 encoded bytes representing the {@code TimeStampToken}
*/
public static byte[] getEncoded(final TimeStampToken timeStampToken) throws DSSException {
try {
final byte[] encoded = timeStampToken.getEncoded();
return encoded;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* If the {@code DSSDocument} is a CMS message and the signed content's content is not null then the {@code CMSSignedData} is returned.
* All exceptions are hidden
*
* @param dssDocument
* @return {@code CMSSignedData} or {@code null}
*/
public static CMSSignedData getOriginalSignedData(final DSSDocument dssDocument) {
CMSSignedData originalSignedData = null;
try {
// check if input toSignDocument is already signed
originalSignedData = new CMSSignedData(dssDocument.getBytes());
if (originalSignedData.getSignedContent().getContent() == null) {
originalSignedData = null;
}
} catch (Exception e) {
// not a parallel signature
}
return originalSignedData;
}
/**
* This method generates a bouncycastle {@code TimeStampToken} based on base 64 encoded {@code String}.
*
* @param base64EncodedTimestamp
* @return bouncycastle {@code TimeStampToken}
* @throws DSSException
*/
public static TimeStampToken createTimeStampToken(final String base64EncodedTimestamp) throws DSSException {
try {
final byte[] tokenBytes = DSSUtils.base64Decode(base64EncodedTimestamp);
final CMSSignedData signedData = new CMSSignedData(tokenBytes);
return new TimeStampToken(signedData);
} catch (DSSException e) {
throw new DSSException(e);
} catch (CMSException e) {
throw new DSSException(e);
} catch (TSPException e) {
throw new DSSException(e);
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method allows to create a {@code BasicOCSPResp} from a {@code DERSequence}.
*
* @param otherRevocationInfoMatch {@code DERSequence} to convert to {@code BasicOCSPResp}
* @return {@code BasicOCSPResp}
*/
public static BasicOCSPResp getBasicOcspResp(final DERSequence otherRevocationInfoMatch) {
BasicOCSPResp basicOCSPResp = null;
try {
final BasicOCSPResponse basicOcspResponse = BasicOCSPResponse.getInstance(otherRevocationInfoMatch);
basicOCSPResp = new BasicOCSPResp(basicOcspResponse);
} catch (Exception e) {
LOG.error("Impossible to create BasicOCSPResp from DERSequence!", e);
}
return basicOCSPResp;
}
/**
* This method allows to create a {@code OCSPResp} from a {@code DERSequence}.
*
* @param otherRevocationInfoMatch {@code DERSequence} to convert to {@code OCSPResp}
* @return {@code OCSPResp}
*/
public static OCSPResp getOcspResp(final DERSequence otherRevocationInfoMatch) {
OCSPResp ocspResp = null;
try {
final OCSPResponse ocspResponse = OCSPResponse.getInstance(otherRevocationInfoMatch);
ocspResp = new OCSPResp(ocspResponse);
} catch (Exception e) {
LOG.error("Impossible to create OCSPResp from DERSequence!", e);
}
return ocspResp;
}
/**
* This method returns the {@code BasicOCSPResp} from a {@code OCSPResp}.
*
* @param ocspResp {@code OCSPResp} to analysed
* @return
*/
public static BasicOCSPResp getBasicOCSPResp(final OCSPResp ocspResp) {
BasicOCSPResp basicOCSPResp = null;
try {
final Object responseObject = ocspResp.getResponseObject();
if (responseObject instanceof BasicOCSPResp) {
basicOCSPResp = (BasicOCSPResp) responseObject;
} else {
LOG.warn("Unknown OCSP response type: {}", responseObject.getClass());
}
} catch (OCSPException e) {
LOG.error("Impossible to process OCSPResp!", e);
}
return basicOCSPResp;
}
/**
* This method returns the {@code ASN1Sequence} encapsulated in {@code DEROctetString}. The {@code DEROctetString} is represented as {@code byte} array.
*
* @param bytes {@code byte} representation of {@code DEROctetString}
* @return encapsulated {@code ASN1Sequence}
* @throws DSSException in case of a decoding problem
*/
public static ASN1Sequence getAsn1SequenceFromDerOctetString(byte[] bytes) throws DSSException {
ASN1InputStream input = null;
try {
input = new ASN1InputStream(bytes);
final DEROctetString s = (DEROctetString) input.readObject();
final byte[] content = s.getOctets();
input.close();
input = new ASN1InputStream(content);
final ASN1Sequence seq = (ASN1Sequence) input.readObject();
return seq;
} catch (IOException e) {
throw new DSSException("Error when converting byte array to ASN1Sequence!", e);
} finally {
DSSUtils.closeQuietly(input);
}
}
/**
* @param signerInformation {@code SignerInformation}
* @return {@code DERTaggedObject} representing the signed attributes
* @throws DSSException in case of a decoding problem
*/
public static DERTaggedObject getSignedAttributes(final SignerInformation signerInformation) throws DSSException {
try {
final byte[] encodedSignedAttributes = signerInformation.getEncodedSignedAttributes();
if (encodedSignedAttributes == null) {
return null;
}
final ASN1Set asn1Set = DSSASN1Utils.toASN1Primitive(encodedSignedAttributes);
return new DERTaggedObject(false, 0, asn1Set);
} catch (IOException e) {
throw new DSSException(e);
}
}
}