All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.europa.esig.dss.spi.DSSASN1Utils Maven / Gradle / Ivy

/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 * 

* This file is part of the "DSS - Digital Signature Services" project. *

* This library 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. *

* This library 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package eu.europa.esig.dss.spi; import eu.europa.esig.dss.enumerations.DigestAlgorithm; import eu.europa.esig.dss.enumerations.EncryptionAlgorithm; import eu.europa.esig.dss.model.DSSException; import eu.europa.esig.dss.model.Digest; import eu.europa.esig.dss.model.TimestampBinary; import eu.europa.esig.dss.model.x509.CertificateToken; import eu.europa.esig.dss.model.x509.X500PrincipalHelper; import eu.europa.esig.dss.spi.x509.CertificateRef; import eu.europa.esig.dss.spi.x509.SignerIdentifier; import eu.europa.esig.dss.utils.Utils; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1GeneralizedTime; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1OutputStream; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.BERTags; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DLSequence; import org.bouncycastle.asn1.DLSet; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.Attributes; import org.bouncycastle.asn1.esf.RevocationValues; import org.bouncycastle.asn1.ess.OtherCertID; import org.bouncycastle.asn1.ocsp.BasicOCSPResponse; import org.bouncycastle.asn1.ocsp.OCSPResponse; import org.bouncycastle.asn1.x500.AttributeTypeAndValue; import org.bouncycastle.asn1.x500.DirectoryString; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.IssuerSerial; import org.bouncycastle.asn1.x509.Time; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.ocsp.BasicOCSPResp; import org.bouncycastle.cert.ocsp.OCSPException; import org.bouncycastle.cert.ocsp.OCSPResp; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerId; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.crypto.signers.PlainDSAEncoding; import org.bouncycastle.crypto.signers.StandardDSAEncoding; import org.bouncycastle.tsp.TimeStampToken; import org.bouncycastle.util.BigIntegers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.security.auth.x500.X500Principal; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.PublicKey; import java.security.Security; import java.security.cert.CertificateException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Utility class that contains some ASN1 related method. * */ public final class DSSASN1Utils { private static final Logger LOG = LoggerFactory.getLogger(DSSASN1Utils.class); static { Security.addProvider(DSSSecurityProvider.getSecurityProvider()); } /** * This class is a utility class and cannot be instantiated. */ private DSSASN1Utils() { // empty } /** * 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} * @param * the expected return type * @return new {@code T extends ASN1Primitive} */ @SuppressWarnings("unchecked") public static T toASN1Primitive(final byte[] bytes) { try { return (T) ASN1Primitive.fromByteArray(bytes); } catch (IOException e) { throw new DSSException(String.format("Cannot convert binaries to ASN1Primitive : %s", e.getMessage()), e); } } /** * This method checks if a given {@code DEROctetString} is null. * * @param derOctetString * the {@code DEROctetString} to check * @return true if the {@code DEROctetString} contains DERNull */ public static boolean isDEROctetStringNull(final DEROctetString derOctetString) { final byte[] derOctetStringBytes = derOctetString.getOctets(); final ASN1Primitive asn1Null = toASN1Primitive(derOctetStringBytes); return DERNull.INSTANCE.equals(asn1Null); } /** * This method returns 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) { return getEncoded(asn1Encodable, ASN1Encoding.DER); } /** * This method returns BER encoded ASN1 attribute. The {@code IOException} is * transformed in {@code DSSException}. * * @param asn1Encodable * asn1Encodable to be BER encoded * @return array of bytes representing the BER encoded asn1Encodable */ public static byte[] getBEREncoded(ASN1Encodable asn1Encodable) { return getEncoded(asn1Encodable, ASN1Encoding.BER); } /** * This method returns encoded ASN1 attribute. The {@code IOException} is * transformed in {@code DSSException}. * * @param asn1Encodable * asn1Encodable to be the given encoding * @param encoding * the expected encoding * @return array of bytes representing the encoded asn1Encodable */ private static byte[] getEncoded(ASN1Encodable asn1Encodable, String encoding) { try { return asn1Encodable.toASN1Primitive().getEncoded(encoding); } catch (IOException e) { throw new DSSException(String.format("Unable to encode to %s. Reason : %s", encoding, e.getMessage()), e); } } /** * Gets the DER-encoded binaries of the {@code BasicOCSPResp} * * @param basicOCSPResp {@link BasicOCSPResp} * @return DER-encoded binaries */ public static byte[] getEncoded(BasicOCSPResp basicOCSPResp) { try { BasicOCSPResponse basicOCSPResponse = BasicOCSPResponse.getInstance(basicOCSPResp.getEncoded()); return getDEREncoded(basicOCSPResponse); } catch (IOException e) { throw new DSSException(String.format("Cannot retrieve DER encoded binaries of BasicOCSPResp : %s", e.getMessage()), e); } } /** * Converts {@code ASN1GeneralizedTime} to {@code Date} * * @param asn1Date {@link ASN1GeneralizedTime} * @return {@link Date} */ public static Date toDate(final ASN1GeneralizedTime asn1Date) { try { return asn1Date.getDate(); } catch (ParseException e) { throw new DSSException(String.format("Cannot parse Date : %s", e.getMessage()), e); } } /** * Reads {@code ASN1OctetString} value and returns * * @param value {@link ASN1OctetString} * @return {@link String} */ public static String toString(final ASN1OctetString value) { return new String(value.getOctets()); } /** * Returns an ASN.1 encoded bytes representing the {@code TimeStampToken} * * @param timeStampToken * {@code TimeStampToken} * @return the DER encoded {@code TimeStampToken} */ public static byte[] getEncoded(final TimeStampToken timeStampToken) { return getEncoded(timeStampToken.toCMSSignedData()); } /** * Returns an ASN.1 encoded bytes representing the {@code CMSSignedData} * * @param cmsSignedData * {@code CMSSignedData} * @return the binary of the {@code CMSSignedData} @ if the {@code * CMSSignedData} encoding fails */ public static byte[] getEncoded(final CMSSignedData cmsSignedData) { try { return cmsSignedData.getEncoded(); } catch (IOException e) { throw new DSSException("Unable to encode to DER", e); } } /** * Gets the DER encoded binaries of {@code TimeStampToken} * * @param timeStampToken {@link TimeStampToken} * @return DER encoded binaries */ public static byte[] getDEREncoded(final TimeStampToken timeStampToken) { return getDEREncoded(timeStampToken.toCMSSignedData()); } /** * Returns the ASN.1 encoded representation of {@code CMSSignedData}. * * @param data * the CMSSignedData to be encoded * @return the DER encoded CMSSignedData */ public static byte[] getDEREncoded(final CMSSignedData data) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { final ASN1OutputStream asn1OutputStream = ASN1OutputStream.create(baos, ASN1Encoding.DER); asn1OutputStream.writeObject(data.toASN1Structure()); asn1OutputStream.close(); return baos.toByteArray(); } catch (IOException e) { throw new DSSException("Unable to encode to DER", e); } } /** * Returns the ASN.1 encoded representation of {@code TimestampBinary}. * * @param timestampBinary * the {@link TimestampBinary} to be encoded * @return the DER encoded timestampBinary */ public static byte[] getDEREncoded(final TimestampBinary timestampBinary) { return getDEREncoded(timestampBinary.getBytes()); } /** * Returns the ASN.1 encoded representation of {@code byte} array. * * @param bytes * the binary array to encode * @return the DER encoded bytes */ public static byte[] getDEREncoded(final byte[] bytes) { try { return getDEREncoded(ASN1Primitive.fromByteArray(bytes)); } catch (IOException e) { throw new DSSException("Unable to encode to DER", e); } } /** * 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} or exception in case of a decoding problem */ public static ASN1Sequence getAsn1SequenceFromDerOctetString(byte[] bytes) { return getASN1Sequence(getDEROctetStringContent(bytes)); } private static ASN1Sequence getASN1Sequence(byte[] bytes) { try (ASN1InputStream input = new ASN1InputStream(bytes)) { return (ASN1Sequence) input.readObject(); } catch (IOException e) { throw new DSSException("Unable to retrieve the ASN1Sequence", e); } } /** * This method returns the {@code ASN1Integer} encapsulated in * {@code DEROctetString}. The {@code DEROctetString} is represented as * {@code byte} array. * * @param bytes * {@code byte} representation of {@code DEROctetString} * @return encapsulated {@code ASN1Integer} or exception in case of a decoding problem */ public static ASN1Integer getAsn1IntegerFromDerOctetString(byte[] bytes) { return getASN1Integer(getDEROctetStringContent(bytes)); } private static ASN1Integer getASN1Integer(byte[] bytes) { try (ASN1InputStream input = new ASN1InputStream(bytes)) { return (ASN1Integer) input.readObject(); } catch (IOException e) { throw new DSSException("Unable to retrieve the ASN1Integer", e); } } private static byte[] getDEROctetStringContent(byte[] bytes) { try (ASN1InputStream input = new ASN1InputStream(bytes)) { final DEROctetString s = (DEROctetString) input.readObject(); return s.getOctets(); } catch (IOException e) { throw new DSSException("Unable to retrieve the DEROctetString content", e); } } /** * This method computes the digest of an ASN1 signature policy (used in CAdES) * * TS 101 733 5.8.1 : If the signature policy is defined using ASN.1, then the hash is calculated on the value * without the outer type and length * fields, and the hashing algorithm shall be as specified in the field sigPolicyHash. * * @param digestAlgorithm * the digest algorithm to be used * @param policyBytes * the ASN.1 policy content * @return the expected digest value */ public static byte[] getAsn1SignaturePolicyDigest(DigestAlgorithm digestAlgorithm, byte[] policyBytes) { ASN1Sequence asn1Seq = toASN1Primitive(policyBytes); ASN1Sequence signPolicyHashAlgObject = (ASN1Sequence) asn1Seq.getObjectAt(0); AlgorithmIdentifier signPolicyHashAlgIdentifier = AlgorithmIdentifier.getInstance(signPolicyHashAlgObject); ASN1Sequence signPolicyInfo = (ASN1Sequence) asn1Seq.getObjectAt(1); byte[] hashAlgorithmDEREncoded = getDEREncoded(signPolicyHashAlgIdentifier); byte[] signPolicyInfoDEREncoded = getDEREncoded(signPolicyInfo); return DSSUtils.digest(digestAlgorithm, hashAlgorithmDEREncoded, signPolicyInfoDEREncoded); } /** * Gets the ASN.1 algorithm identifier structure corresponding to the algorithm * found in the provided Timestamp Hash Index Table, if such algorithm is present * * @param atsHashIndexValue * ats-hash-index table from a timestamp * @return the ASN.1 algorithm identifier structure */ public static AlgorithmIdentifier getAlgorithmIdentifier(final ASN1Sequence atsHashIndexValue) { if (atsHashIndexValue != null && atsHashIndexValue.size() > 3) { final int algorithmIndex = 0; final ASN1Encodable asn1Encodable = atsHashIndexValue.getObjectAt(algorithmIndex); if (asn1Encodable instanceof ASN1Sequence) { final ASN1Sequence asn1Sequence = (ASN1Sequence) asn1Encodable; return AlgorithmIdentifier.getInstance(asn1Sequence); } else if (asn1Encodable instanceof ASN1ObjectIdentifier) { // TODO (16/11/2014): The relevance and usefulness of the test case must be checked (do the signatures // like this exist?) ASN1ObjectIdentifier derObjectIdentifier = ASN1ObjectIdentifier.getInstance(asn1Encodable); return new AlgorithmIdentifier(derObjectIdentifier); } } return null; } /** * Gets the ASN.1 algorithm identifier structure corresponding to a digest algorithm * * @param digestAlgorithm * the digest algorithm to encode * @return the ASN.1 algorithm identifier structure */ public static AlgorithmIdentifier getAlgorithmIdentifier(DigestAlgorithm digestAlgorithm) { /* * The recommendation (cf. RFC 3380 section 2.1) is to omit the parameter for SHA-1, but some implementations * still expect a * NULL there. Therefore we always include a NULL parameter even with SHA-1, despite the recommendation, because * the RFC * states that implementations SHOULD support it as well anyway */ final ASN1ObjectIdentifier asn1ObjectIdentifier = new ASN1ObjectIdentifier(digestAlgorithm.getOid()); return new AlgorithmIdentifier(asn1ObjectIdentifier, DERNull.INSTANCE); } /** * Returns list of {@code DEROctetString} from an {@code ASN1Sequence} * Useful when needed to get a list of hash values * * @param asn1Sequence {@link ASN1Sequence} to get list from * @return list of {@link DEROctetString}s */ @SuppressWarnings("unchecked") public static List getDEROctetStrings(final ASN1Sequence asn1Sequence) { final List derOctetStrings = new ArrayList<>(); if (asn1Sequence != null) { derOctetStrings.addAll(Collections.list(asn1Sequence.getObjects())); } return derOctetStrings; } /** * Computes SHA-1 hash of the {@code certificateToken}'s public key * * @param certificateToken * {@link CertificateToken} to compute digest for * @return byte array of public key's SHA-1 hash */ public static byte[] computeSkiFromCert(final CertificateToken certificateToken) { return computeSkiFromCertPublicKey(certificateToken.getPublicKey()); } /** * Computes SHA-1 hash of the given {@code publicKey}'s * @param publicKey {@link PublicKey} to compute digest for * @return byte array of public key's SHA-1 hash */ public static byte[] computeSkiFromCertPublicKey(final PublicKey publicKey) { try { DLSequence seq = (DLSequence) ASN1Primitive.fromByteArray(publicKey.getEncoded()); DERBitString item = (DERBitString) seq.getObjectAt(1); return DSSUtils.digest(DigestAlgorithm.SHA1, item.getOctets()); } catch (IOException e) { throw new DSSException(String.format("Unable to compute ski from public key : %s", e.getMessage()), e); } } /** * Checks if the provided ski matches to a ski computed from a certificateToken's public key * * @param ski a byte array representing ski value (SHA-1 of the public key) * @param certificateToken {@link CertificateToken} to check * @return TRUE if the SKI equals, FALSE otherwise */ public static boolean isSkiEqual(final byte[] ski, final CertificateToken certificateToken) { byte[] certSki = computeSkiFromCert(certificateToken); return Arrays.equals(certSki, ski); } /** * Returns a {@code X509CertificateHolder} encapsulating the given {@code X509Certificate}. * * @param certToken * the certificate to be encapsulated * @return a X509CertificateHolder holding this certificate */ public static X509CertificateHolder getX509CertificateHolder(CertificateToken certToken) { try { return new X509CertificateHolder(certToken.getEncoded()); } catch (IOException e) { throw new DSSException(String.format("Unable to instantiate a X509CertificateHolder : %s", e.getMessage()), e); } } /** * Extract the certificate token from {@code X509CertificateHolder} * * @param x509CertificateHolder {@link X509CertificateHolder} * @return {@link CertificateToken} */ public static CertificateToken getCertificate(final X509CertificateHolder x509CertificateHolder) { try { JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(DSSSecurityProvider.getSecurityProviderName()); X509Certificate x509Certificate = converter.getCertificate(x509CertificateHolder); return new CertificateToken(x509Certificate); } catch (CertificateException e) { throw new DSSException(String.format( "Unable to get a CertificateToken from X509CertificateHolder : %s", e.getMessage()), e); } } /** * This method transforms token's signerId into a {@code SignerIdentifier} * object * * @param signerId {@link SignerId} to be transformed * @return {@link SignerIdentifier} */ public static SignerIdentifier toSignerIdentifier(SignerId signerId) { X500Principal issuerX500Principal = toX500Principal(signerId.getIssuer()); return toSignerIdentifier(issuerX500Principal, signerId.getSerialNumber(), signerId.getSubjectKeyIdentifier()); } /** * Transforms x500Name to X500Principal * * @param x500Name {@link X500Name} * @return {@link X500Principal} */ public static X500Principal toX500Principal(X500Name x500Name) { if (x500Name == null) { return null; } try { return new X500Principal(x500Name.getEncoded()); } catch (IOException e) { throw new DSSException(String.format("Cannot extract X500Principal! Reason : %s", e.getMessage()), e); } } /** * This method transforms token's issuer and serial number information into a * {@code CertificateIdentifier} object * * @param issuerX500Principal {@link X500Principal} of the issuer * @param serialNumber {@link BigInteger} of the token * @param ski a byte array representing a SubjectKeyIdentifier * (SHA-1 digest of the public key) * @return {@link SignerIdentifier} */ public static SignerIdentifier toSignerIdentifier(final X500Principal issuerX500Principal, final BigInteger serialNumber, final byte[] ski) { SignerIdentifier signerIdentifier = new SignerIdentifier(); signerIdentifier.setIssuerName(issuerX500Principal); signerIdentifier.setSerialNumber(serialNumber); signerIdentifier.setSki(ski); return signerIdentifier; } /** * This method returns a new IssuerSerial based on the certificate token * * @param certToken * the certificate token * @return a IssuerSerial */ public static IssuerSerial getIssuerSerial(final CertificateToken certToken) { final X500Name issuerX500Name = getX509CertificateHolder(certToken).getIssuer(); final GeneralName generalName = new GeneralName(issuerX500Name); final GeneralNames generalNames = new GeneralNames(generalName); final BigInteger serialNumber = certToken.getCertificate().getSerialNumber(); return new IssuerSerial(generalNames, serialNumber); } /** * This method compares two {@code X500Principal}s. {@code X500Principal.CANONICAL} and * {@code X500Principal.RFC2253} forms are compared. * * @param firstX500Principal * the first X500Principal object to be compared * @param secondX500Principal * the second X500Principal object to be compared * @return true if the two parameters contain the same key/values */ public static boolean x500PrincipalAreEquals(final X500Principal firstX500Principal, final X500Principal secondX500Principal) { if ((firstX500Principal == null) || (secondX500Principal == null)) { return false; } if (firstX500Principal.equals(secondX500Principal)) { return true; } final Map firstStringStringHashMap = get(firstX500Principal); final Map secondStringStringHashMap = get(secondX500Principal); return firstStringStringHashMap.equals(secondStringStringHashMap); } /** * Gets a map of X500 attribute names and the values * * @param x500Principal {@link X500Principal} * @return a map of X500 attribute names and the values */ public static Map get(final X500Principal x500Principal) { Map treeMap = new HashMap<>(); final byte[] encoded = x500Principal.getEncoded(); final ASN1Sequence asn1Sequence = ASN1Sequence.getInstance(encoded); final ASN1Encodable[] asn1Encodables = asn1Sequence.toArray(); for (final ASN1Encodable asn1Encodable : asn1Encodables) { final DLSet dlSet = (DLSet) asn1Encodable; for (int ii = 0; ii < dlSet.size(); ii++) { final DLSequence dlSequence = (DLSequence) dlSet.getObjectAt(ii); if (dlSequence.size() != 2) { throw new DSSException("The DLSequence must contains exactly 2 elements."); } final ASN1Encodable asn1EncodableAttributeType = dlSequence.getObjectAt(0); final String stringAttributeType = getString(asn1EncodableAttributeType); final ASN1Encodable asn1EncodableAttributeValue = dlSequence.getObjectAt(1); final String stringAttributeValue = getString(asn1EncodableAttributeValue); treeMap.put(stringAttributeType, stringAttributeValue); } } return treeMap; } /** * Converts {@code ASN1Encodable} to a {@code String} value. * The method preserves the object class and structure and returns hash-encoded String value, * unless the object is an instance of {@code ASN1String}. * * @param attributeValue {@link ASN1Encodable} to read * @return {@link String} value */ public static String getString(ASN1Encodable attributeValue) { if (attributeValue == null) { LOG.warn("Null attribute has been provided!"); return null; } try { return IETFUtils.valueToString(attributeValue); } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.warn("Unable to handle attribute of class '{}' : {}", attributeValue.getClass().getName(), e.getMessage()); } else { LOG.warn("Unable to handle attribute : {}", e.getMessage()); } return null; } } /** * Extract attribute with the {@code identifier} from {@code X500PrincipalHelper} * * @param identifier {@link ASN1ObjectIdentifier} oid of the attribute to get value * @param principal {@link X500PrincipalHelper} to extract the attribute value from * @return {@link String} value */ public static String extractAttributeFromX500Principal(ASN1ObjectIdentifier identifier, X500PrincipalHelper principal) { final X500Name x500Name = X500Name.getInstance(principal.getEncoded()); RDN[] rdns = x500Name.getRDNs(identifier); for (RDN rdn : rdns) { if (rdn.isMultiValued()) { AttributeTypeAndValue[] typesAndValues = rdn.getTypesAndValues(); for (AttributeTypeAndValue typeAndValue : typesAndValues) { if (identifier.equals(typeAndValue.getType())) { return typeAndValue.getValue().toString(); } } } else { AttributeTypeAndValue typeAndValue = rdn.getFirst(); if (identifier.equals(typeAndValue.getType())) { return typeAndValue.getValue().toString(); } } } return null; } /** * Extracts the Subject Common name from the certificate token * * @param cert {@link CertificateToken} * @return {@link String} */ public static String getSubjectCommonName(CertificateToken cert) { return extractAttributeFromX500Principal(BCStyle.CN, cert.getSubject()); } /** * Extracts the pretty printed name of the certificate token * * @param cert {@link CertificateToken} * @return {@link String} */ public static String getHumanReadableName(CertificateToken cert) { return getHumanReadableName(cert.getSubject()); } /** * Extracts the pretty printed name from the {@code X500PrincipalHelper} * * @param x500PrincipalHelper {@link X500PrincipalHelper} * @return {@link String} */ public static String getHumanReadableName(X500PrincipalHelper x500PrincipalHelper) { return firstNotNull(x500PrincipalHelper, BCStyle.CN, BCStyle.GIVENNAME, BCStyle.SURNAME, BCStyle.NAME, BCStyle.PSEUDONYM, BCStyle.O, BCStyle.OU); } private static String firstNotNull(X500PrincipalHelper x500PrincipalHelper, ASN1ObjectIdentifier... oids) { for (ASN1ObjectIdentifier oid : oids) { String value = extractAttributeFromX500Principal(oid, x500PrincipalHelper); if (value != null) { return value; } } return null; } /** * Returns the first {@code SignerInformation} extracted from {@code CMSSignedData}. * * @param cms * CMSSignedData * @return returns {@code SignerInformation} */ public static SignerInformation getFirstSignerInformation(final CMSSignedData cms) { final Collection signers = cms.getSignerInfos().getSigners(); if (signers.size() > 1) { LOG.warn("!!! The framework handles only one signer (SignerInformation) !!!"); } return signers.iterator().next(); } /** * Checks if the byte defines an ASN1 Sequence * * @param tagByte byte to check * @return TRUE if the byte defines an ASN1 Sequence, FALSE otherwise */ public static boolean isASN1SequenceTag(byte tagByte) { // BERTags.SEQUENCE | BERTags.CONSTRUCTED = 0x30 return (BERTags.SEQUENCE | BERTags.CONSTRUCTED) == tagByte; } /** * Reads the {@code encodable} and returns a {@code Date} * * @param encodable {@link ASN1Encodable} to read * @return {@link Date} */ public static Date getDate(ASN1Encodable encodable) { try { return Time.getInstance(encodable).getDate(); } catch (Exception e) { LOG.warn("Unable to retrieve the date {}", encodable, e); return null; } } /** * Extracts all extended key usages for the certificate token * * @param certToken {@link CertificateToken} * @return a list of {@link String}s */ public static List getExtendedKeyUsage(CertificateToken certToken) { try { return certToken.getCertificate().getExtendedKeyUsage(); } catch (CertificateParsingException e) { LOG.warn("Unable to retrieve ExtendedKeyUsage : {}", e.getMessage()); return Collections.emptyList(); } } /** * Gets the {@code IssuerSerial} object * * @param binaries representing the {@link IssuerSerial} * @return {@link IssuerSerial} if able to parse, null otherwise */ public static IssuerSerial getIssuerSerial(byte[] binaries) { try (ASN1InputStream is = new ASN1InputStream(binaries)) { ASN1Sequence seq = (ASN1Sequence) is.readObject(); return IssuerSerial.getInstance(seq); } catch (Exception e) { LOG.warn("Unable to decode IssuerSerialV2 textContent '{}' : {}", Utils.toBase64(binaries), e.getMessage(), e); return null; } } /** * Transforms an object of class {@code IssuerSerial} into instance of * {@code CertificateIdentifier} * * @param issuerAndSerial {@link IssuerSerial} to transform * @return {@link SignerIdentifier} */ public static SignerIdentifier toSignerIdentifier(IssuerSerial issuerAndSerial) { if (issuerAndSerial == null) { return null; } try { SignerIdentifier signerIdentifier = new SignerIdentifier(); GeneralNames gnames = issuerAndSerial.getIssuer(); if (gnames != null) { GeneralName[] names = gnames.getNames(); if (names.length == 1) { signerIdentifier.setIssuerName(new X500Principal(names[0].getName().toASN1Primitive().getEncoded(ASN1Encoding.DER))); } else { LOG.warn("More than one GeneralName"); } } ASN1Integer serialNumber = issuerAndSerial.getSerial(); if (serialNumber != null) { signerIdentifier.setSerialNumber(serialNumber.getValue()); } return signerIdentifier; } catch (Exception e) { LOG.warn("Unable to read the IssuerSerial object", e); return null; } } /** * Returns an array of {@link Attribute}s for a given {@code oid} found in the {@code attributeTable} * * @param attributeTable {@link AttributeTable} * @param oid target {@link ASN1ObjectIdentifier} * @return {@link Attribute}s array */ public static Attribute[] getAsn1Attributes(AttributeTable attributeTable, ASN1ObjectIdentifier oid) { ASN1EncodableVector encodableVector = attributeTable.getAll(oid); if (encodableVector == null) { return new Attribute[0]; } Attributes attributes = new Attributes(encodableVector); return attributes.getAttributes(); } /** * Returns generation time for the provided {@code timeStampToken} * * @param timeStampToken {@link TimeStampToken} to get generation time for * @return {@link Date} timestamp generation time */ public static Date getTimeStampTokenGenerationTime(TimeStampToken timeStampToken) { if (timeStampToken != null) { return timeStampToken.getTimeStampInfo().getGenTime(); } return null; } /** * Returns {@link RevocationValues} from the given encodable * * @param encodable * the encoded data to be parsed * @return an instance of RevocationValues or null if the parsing failed */ public static RevocationValues getRevocationValues(ASN1Encodable encodable) { if (encodable != null) { try { return RevocationValues.getInstance(encodable); } catch (Exception e) { LOG.warn("Unable to parse RevocationValues", e); } } return null; } /** * Converts the {@code OtherCertID} to {@code CertificateRef} * * @param otherCertId {@link OtherCertID} * @return {@link CertificateRef} */ public static CertificateRef getCertificateRef(OtherCertID otherCertId) { CertificateRef certRef = new CertificateRef(); DigestAlgorithm digestAlgo = DigestAlgorithm.forOID(otherCertId.getAlgorithmHash().getAlgorithm().getId()); certRef.setCertDigest(new Digest(digestAlgo, otherCertId.getCertHash())); certRef.setCertificateIdentifier(toSignerIdentifier(otherCertId.getIssuerSerial())); return certRef; } /** * Checks if the binaries are ASN.1 encoded. * * @param binaries byte array to check. * @return if the SignatureValue binaries are ASN.1 encoded. */ public static boolean isAsn1Encoded(byte[] binaries) { if (Utils.isArrayEmpty(binaries)) { return false; } try (ASN1InputStream is = new ASN1InputStream(binaries)) { return is.readObject() != null; } catch (Exception e) { return false; } } /** * Checks if the SignatureValue binaries are ASN.1 encoded. * * @param binaries byte array to check. * @return if the SignatureValue binaries are ASN.1 encoded. */ public static boolean isAsn1EncodedSignatureValue(byte[] binaries) { try (ASN1InputStream is = new ASN1InputStream(binaries)) { ASN1Sequence seq = (ASN1Sequence) is.readObject(); return seq != null && seq.size() == 2; } catch (Exception e) { return false; } } /** * Converts the ANS.1 binary signature value to the concatenated (plain) R || S format if required * * NOTE: used in XAdES and JAdES * * @param algorithm * Encryption algorithm used to create the signatureValue * @param signatureValue * the originally computed signature value * @return the converted signature value */ public static byte[] ensurePlainSignatureValue(final EncryptionAlgorithm algorithm, byte[] signatureValue) { if ((EncryptionAlgorithm.ECDSA == algorithm || EncryptionAlgorithm.PLAIN_ECDSA == algorithm || EncryptionAlgorithm.DSA == algorithm) && isAsn1EncodedSignatureValue(signatureValue)) { return toPlainDSASignatureValue(signatureValue); } else { return signatureValue; } } /** * Converts an ASN.1 value to a concatenation string of R and S from ECDSA/DSA encryption algorithm * * The JAVA JCE ECDSA/DSA Signature algorithm creates ASN.1 encoded (r,s) value pairs. * * @param asn1SignatureValue * the ASN1 signature value * @return the decoded bytes * @see 6.4.1 DSA * @see 3.3. ECDSA Signatures */ public static byte[] toPlainDSASignatureValue(byte[] asn1SignatureValue) { try { BigInteger order = getOrderFromSignatureValue(asn1SignatureValue); final BigInteger[] values = StandardDSAEncoding.INSTANCE.decode(order, asn1SignatureValue); return PlainDSAEncoding.INSTANCE.encode(order, values[0], values[1]); } catch (Exception e) { throw new DSSException("Unable to convert to plain : " + e.getMessage(), e); } } /** * Converts a plain {@code signatureValue} to its corresponding ASN.1 format * * @param signatureValue * the plain signature value * @return the encoded bytes * @see 6.4.1 DSA * @see 3.3. ECDSA Signatures */ public static byte[] toStandardDSASignatureValue(byte[] signatureValue) { try { BigInteger order = getOrderFromSignatureValue(signatureValue); final BigInteger[] values = PlainDSAEncoding.INSTANCE.decode(order, signatureValue); return StandardDSAEncoding.INSTANCE.encode(order, values[0], values[1]); } catch (Exception e) { throw new DSSException("Unable to convert to standard DSA : " + e.getMessage(), e); } } /** * Gets the order parameter corresponding the given {@code signatureValue} * * @param signatureValue byte array * @return {@link BigInteger} */ public static BigInteger getOrderFromSignatureValue(byte[] signatureValue) { try { BigInteger rValue; BigInteger sValue; if (DSSASN1Utils.isAsn1EncodedSignatureValue(signatureValue)) { ASN1Sequence seq = (ASN1Sequence) ASN1Primitive.fromByteArray(signatureValue); if (seq.size() != 2) { throw new IllegalArgumentException("ASN1 Sequence size should be 2!"); } rValue = ((ASN1Integer) seq.getObjectAt(0)).getValue(); sValue = ((ASN1Integer) seq.getObjectAt(1)).getValue(); } else { if (signatureValue.length % 2 != 0) { throw new IllegalArgumentException("signatureValue binaries length shall be dividable by 2!"); } int valueLength = signatureValue.length / 2; rValue = BigIntegers.fromUnsignedByteArray(signatureValue, 0, valueLength); sValue = BigIntegers.fromUnsignedByteArray(signatureValue, valueLength, valueLength); } BigInteger max = rValue.max(sValue); return max.add(BigInteger.ONE); } catch (IOException e) { throw new DSSException("Unable to extract order from a signature value : " + e.getMessage(), e); } } /** * This method returns a bit length of the provided signature value * * @param signatureValue byte array representing the signature value * @return bit length of the signature value */ public static int getSignatureValueBitLength(byte[] signatureValue) { try { BigInteger order = DSSASN1Utils.getOrderFromSignatureValue(signatureValue); return BigIntegers.getUnsignedByteLength(order) * 8; // convert to bits } catch (Exception e) { throw new DSSException(String.format("Unable to extract a signature value bit length : %s", e.getMessage()), e); } } /** * Returns a value of an ASN.1 DirectoryString instance Returns null if an error * occurs during the transformation * * @param directoryStringInstance {@link ASN1Encodable} to get DirectoryString * value from * @return {@link String} value */ public static String getDirectoryStringValue(ASN1Encodable directoryStringInstance) { String postalAddress = null; try { DirectoryString directoryString = DirectoryString.getInstance(directoryStringInstance); postalAddress = directoryString.getString(); } catch (Exception e) { String errorMessage = "Unable to build a DirectoryString instance. Reason : {}"; if (LOG.isDebugEnabled()) { LOG.warn(errorMessage, e.getMessage(), e); } else { LOG.warn(errorMessage, e.getMessage()); } } return postalAddress; } /** * Converts an object of {@code OCSPResponse} class to {@code BasicOCSPResp} * * @param ocspResponse {@link OCSPResponse} to convert * @return {@link BasicOCSPResp} * @throws OCSPException in case of a conversion error */ public static BasicOCSPResp toBasicOCSPResp(OCSPResponse ocspResponse) throws OCSPException { final OCSPResp ocspResp = new OCSPResp(ocspResponse); return (BasicOCSPResp) ocspResp.getResponseObject(); } /** * Converts an array of {@code OCSPResponse}s to an array of {@code BasicOCSPResp}s * * @param ocspResponses an array of {@link OCSPResponse}s to convert * @return an array of {@code BasicOCSPResp} */ public static BasicOCSPResp[] toBasicOCSPResps(OCSPResponse[] ocspResponses) { List basicOCSPResps = new ArrayList<>(); for (OCSPResponse ocspRespons : ocspResponses) { try { basicOCSPResps.add(toBasicOCSPResp(ocspRespons)); } catch (OCSPException e) { LOG.warn("Error while converting OCSPResponse to BasicOCSPResp : {}", e.getMessage()); return null; } } return basicOCSPResps.toArray(new BasicOCSPResp[0]); } /** * Converts an array of {@code BasicOCSPResponse}s to an array of {@code BasicOCSPResp}s * * @param basicOCSPResponses an array of {@link BasicOCSPResponse}s to convert * @return an array of {@code BasicOCSPResp} */ public static BasicOCSPResp[] toBasicOCSPResps(BasicOCSPResponse[] basicOCSPResponses) { List basicOCSPResps = new ArrayList<>(); for (BasicOCSPResponse basicOCSPRespons : basicOCSPResponses) { basicOCSPResps.add(new BasicOCSPResp(basicOCSPRespons)); } return basicOCSPResps.toArray(new BasicOCSPResp[0]); } /** * Builds SPDocSpecification attribute from the given {@code oidOrUri} * * SPDocSpecification ::= CHOICE { * oid OBJECT IDENTIFIER, * uri IA5String * } * * @param oidOrUri {@link String} represents OID or URI * @return {@link ASN1Primitive} */ public static ASN1Primitive buildSPDocSpecificationId(String oidOrUri) { ASN1Primitive spDocSpecification; if (DSSUtils.isOidCode(oidOrUri)) { spDocSpecification = new ASN1ObjectIdentifier(oidOrUri); } else { spDocSpecification = new DERIA5String(oidOrUri); } return spDocSpecification; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy