Package br.net.woodstock.rockframework.security.sign.impl

Source Code of br.net.woodstock.rockframework.security.sign.impl.BouncyCastlePKCS7Signer

/*
* This file is part of rockframework.
*
* rockframework is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* rockframework 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>;.
*/
package br.net.woodstock.rockframework.security.sign.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1UTCTime;
import org.bouncycastle.asn1.BEROctetString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSAbsentContent;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSSignedDataParser;
import org.bouncycastle.cms.CMSSignedGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.CMSTypedStream;
import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.util.CollectionStore;

import br.net.woodstock.rockframework.core.RockFrameworkLogger;
import br.net.woodstock.rockframework.core.RockFrameworkVersion;
import br.net.woodstock.rockframework.core.util.Assert;
import br.net.woodstock.rockframework.core.utils.Arrays;
import br.net.woodstock.rockframework.core.utils.Collections;
import br.net.woodstock.rockframework.core.utils.Conditions;
import br.net.woodstock.rockframework.security.Identity;
import br.net.woodstock.rockframework.security.sign.PKCS7SignatureMode;
import br.net.woodstock.rockframework.security.sign.PKCS7SignatureParameters;
import br.net.woodstock.rockframework.security.sign.Signatory;
import br.net.woodstock.rockframework.security.sign.Signature;
import br.net.woodstock.rockframework.security.sign.SignatureType;
import br.net.woodstock.rockframework.security.sign.SignerException;
import br.net.woodstock.rockframework.security.timestamp.TimeStamp;
import br.net.woodstock.rockframework.security.timestamp.TimeStampClient;
import br.net.woodstock.rockframework.security.timestamp.impl.BouncyCastleTimeStampHelper;
import br.net.woodstock.rockframework.security.util.BouncyCastleProviderHelper;

public class BouncyCastlePKCS7Signer extends AbstractSigner {

  private static final long      serialVersionUID  = RockFrameworkVersion.VERSION;

  private PKCS7SignatureParameters  parameters;

  public BouncyCastlePKCS7Signer(final PKCS7SignatureParameters parameters) {
    super();
    this.parameters = parameters;
  }

  @Override
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public byte[] sign(final byte[] data) {
    Assert.notNull(this.parameters, "parameters");
    Assert.notEmpty(data, "data");
    try {
      CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator();
      TimeStampClient timeStampClient = this.parameters.getTimeStampClient();
      PKCS7SignatureMode mode = this.parameters.getSignatureMode();
      SignatureType signatureType = this.parameters.getSignatureType();

      if (signatureType == null) {
        signatureType = SignatureType.SHA1_RSA;
      }

      for (Identity identity : this.parameters.getIdentities()) {
        PrivateKey privateKey = identity.getPrivateKey();
        Certificate[] chain = identity.getChain();
        Certificate certificate = chain[0];

        JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(signatureType.getAlgorithm());
        if (Conditions.isNotEmpty(this.parameters.getProvider())) {
          contentSignerBuilder.setProvider(this.parameters.getProvider());
        } else {
          contentSignerBuilder.setProvider(BouncyCastleProviderHelper.PROVIDER_NAME);
        }

        ContentSigner contentSigner = contentSignerBuilder.build(privateKey);

        JcaDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder();
        digestCalculatorProviderBuilder.setProvider(BouncyCastleProviderHelper.PROVIDER_NAME);
        DigestCalculatorProvider digestCalculatorProvider = digestCalculatorProviderBuilder.build();

        JcaSignerInfoGeneratorBuilder signerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider);

        if (this.parameters.isDataDigested()) {
          Attribute attr = new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(data)));
          ASN1EncodableVector v = new ASN1EncodableVector();
          v.add(attr);
          signerInfoGeneratorBuilder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
        }

        SignerInfoGenerator signerInfoGenerator = signerInfoGeneratorBuilder.build(contentSigner, (X509Certificate) certificate);

        signedDataGenerator.addSignerInfoGenerator(signerInfoGenerator);
        signedDataGenerator.addCertificates(BouncyCastleProviderHelper.toStore(chain));
      }

      CMSTypedData content = null;
      boolean encapsulate = true;

      if (this.parameters.isDataDigested()) {
        content = new CMSAbsentContent();
        encapsulate = false;
      } else {
        if ((this.parameters.isMergeSignatures()) && (this.isSigned(data))) {
          CMSSignedData signedData = new CMSSignedData(data);
          signedDataGenerator.addSigners(signedData.getSignerInfos());
          content = signedData.getSignedContent();
        } else {
          content = new CMSProcessableByteArray(data);
        }

        if (PKCS7SignatureMode.DETACHED.equals(mode)) {
          encapsulate = false;
        }
      }

      CMSSignedData signedData = null;

      if (this.parameters.isDataDigested()) {
        signedData = signedDataGenerator.generate(CMSSignedGenerator.DATA, null, false, Security.getProvider(BouncyCastleProviderHelper.PROVIDER_NAME), true);
      } else {
        signedData = signedDataGenerator.generate(content, encapsulate);
      }

      if (timeStampClient != null) {
        SignerInformationStore signerInformationStore = signedData.getSignerInfos();
        List list = new ArrayList();
        for (Object o : signerInformationStore.getSigners()) {
          SignerInformation signerInformation = (SignerInformation) o;
          TimeStamp timeStamp = timeStampClient.getTimeStamp(signerInformation.getSignature());
          ASN1Primitive asn1Primitive = BouncyCastleProviderHelper.toASN1Primitive(timeStamp.getEncoded());
          DERSet derSet = new DERSet(asn1Primitive);

          Hashtable hashtable = new Hashtable();
          Attribute attribute = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, derSet);
          hashtable.put(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, attribute);

          AttributeTable unsignedAtts = new AttributeTable(hashtable);

          list.add(SignerInformation.replaceUnsignedAttributes(signerInformation, unsignedAtts));
        }

        SignerInformationStore tmpSignerInformationStore = new SignerInformationStore(list);

        signedData = CMSSignedData.replaceSigners(signedData, tmpSignerInformationStore);
      }

      return signedData.getEncoded();
    } catch (Exception e) {
      throw new SignerException(e);
    }
  }

  @Override
  public boolean verify(final byte[] data, final byte[] signature) {
    Assert.notEmpty(data, "data");
    Assert.notEmpty(signature, "signature");
    try {
      byte[] content = this.getContent(signature);
      if (content != null) {
        return this.verifyAttached(data, signature);
      }
      return this.verifyDetached(data, signature);
    } catch (Exception e) {
      throw new SignerException(e);
    }
  }

  private boolean verifyAttached(final byte[] data, final byte[] signature) throws CMSException, IOException, OperatorCreationException {
    CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(data), signature);

    CollectionStore certificatesStore = (CollectionStore) signedData.getCertificates();

    SignerInformationStore signerInformationStore = signedData.getSignerInfos();
    boolean verified = true;
    for (Object o : signerInformationStore.getSigners()) {
      SignerInformation signerInformation = (SignerInformation) o;

      verified = BouncyCastleSignerHelper.verifySignature(signerInformation, certificatesStore, data, false);
      if (!verified) {
        break;
      }
    }
    if (verified) {
      byte[] content = this.getContent(signedData.getSignedContent());
      if (content != null) {
        verified = Arrays.isEquals(data, content);
      }
    }
    return verified;
  }

  private boolean verifyDetached(final byte[] data, final byte[] signature) throws CMSException, IOException, OperatorCreationException {
    InputStream inputStream = new ByteArrayInputStream(data);
    DigestCalculatorProvider digestCalculatorProvider = new BcDigestCalculatorProvider();
    CMSSignedDataParser signedDataParser = new CMSSignedDataParser(digestCalculatorProvider, new CMSTypedStream(inputStream), signature);
    CMSTypedStream signedContent = signedDataParser.getSignedContent();

    signedContent.drain();

    CollectionStore certificatesStore = (CollectionStore) signedDataParser.getCertificates();

    boolean verified = true;

    SignerInformationStore signerInformationStore = signedDataParser.getSignerInfos();
    for (Object o : signerInformationStore.getSigners()) {
      SignerInformation signerInformation = (SignerInformation) o;

      if (!BouncyCastleSignerHelper.verifySignature(signerInformation, certificatesStore, data, true)) {
        verified = false;
        break;
      }
    }

    return verified;
  }

  public boolean isSigned(final byte[] data) {
    try {
      CMSSignedData signedData = new CMSSignedData(data);
      RockFrameworkLogger.getLogger().warn("Content is signed with version " + signedData.getVersion());
      return true;
    } catch (Exception e) {
      return false;
    }
  }

  @Override
  @SuppressWarnings("unchecked")
  public Signature[] getSignatures(final byte[] data) {
    try {
      CMSSignedData signedData = new CMSSignedData(data);
      Collection<X509CertificateHolder> certificates = signedData.getCertificates().getMatches(null);
      SignerInformationStore signerInformationStore = signedData.getSignerInfos();
      Collection<SignerInformation> informations = signerInformationStore.getSigners();
      Collection<Signature> signatures = new ArrayList<Signature>();

      // 1.2.840.113549.1.9.3 -- content type
      // 1.2.840.113549.1.9.4 -- messagedigest
      // 1.2.840.113549.1.9.5 -- sign time
      // 1.2.840.113549.1.9.16.2.12 -- signcertificate

      if (Conditions.isNotEmpty(informations)) {
        for (SignerInformation information : informations) {
          Signature signature = new Signature();
          signature.setEncoded(information.getSignature());
          signature.setSignatories(new ArrayList<Signatory>());
          signature.setValid(null);

          // TimeStamp
          AttributeTable signedAttributeTable = information.getSignedAttributes();
          AttributeTable unsignedAttributeTable = information.getUnsignedAttributes();

          // DERSequence contentTypeDerSequence = this.getAttribute(signedAttributeTable,
          // unsignedAttributeTable, PKCSObjectIdentifiers.pkcs_9_at_contentType);
          // DERSequence messageDigestDerSequence = this.getAttribute(signedAttributeTable,
          // unsignedAttributeTable, PKCSObjectIdentifiers.pkcs_9_at_messageDigest);

          DERSequence timeStampDerSequence = this.getAttribute(signedAttributeTable, unsignedAttributeTable, PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
          if (timeStampDerSequence != null) {
            if (timeStampDerSequence.size() == 2) {
              ASN1Primitive derObjectIdentifier = ((ASN1Primitive) timeStampDerSequence.getObjectAt(0)).toASN1Primitive();
              ASN1Primitive derObjectValue = ((ASN1Primitive) timeStampDerSequence.getObjectAt(1)).toASN1Primitive();
              if ((derObjectIdentifier instanceof ASN1ObjectIdentifier) && (derObjectValue instanceof DERSet)) {
                // ASN1ObjectIdentifier asn1ObjectIdentifier = (ASN1ObjectIdentifier)
                // derObjectIdentifier;
                DERSet set = (DERSet) derObjectValue;
                ASN1Encodable encodable = set.getObjectAt(0);
                TimeStampToken timeStampToken = new TimeStampToken(new CMSSignedData(encodable.toASN1Primitive().getEncoded()));
                TimeStamp timeStamp = BouncyCastleTimeStampHelper.toTimeStamp(timeStampToken);
                signature.setTimeStamp(timeStamp);
              }
            }
          }

          DERSequence signTimeDerSequence = this.getAttribute(signedAttributeTable, unsignedAttributeTable, PKCSObjectIdentifiers.pkcs_9_at_signingTime);
          if (signTimeDerSequence != null) {
            ASN1Primitive derObjectIdentifier = ((ASN1Primitive) signTimeDerSequence.getObjectAt(0)).toASN1Primitive();
            ASN1Primitive derObjectValue = ((ASN1Primitive) signTimeDerSequence.getObjectAt(1)).toASN1Primitive();
            if ((derObjectIdentifier instanceof ASN1ObjectIdentifier) && (derObjectValue instanceof DERSet)) {
              DERSet set = (DERSet) derObjectValue;
              ASN1UTCTime time = (ASN1UTCTime) set.getObjectAt(0);
              signature.setDate(time.getAdjustedDate());
            }
          }

          SignerId signerId = information.getSID();
          if (signerId != null) {
            BigInteger serialNumber = signerId.getSerialNumber();
            X509CertificateHolder certificateHolder = null;
            for (X509CertificateHolder tmp : certificates) {
              if (tmp.getSerialNumber().equals(serialNumber)) {
                certificateHolder = tmp;
                break;
              }
            }

            if (certificateHolder != null) {
              Signatory signatory = new Signatory();
              signatory.setSubject(BouncyCastleProviderHelper.toString(certificateHolder.getSubject()));
              signatory.setIssuer(BouncyCastleProviderHelper.toString(certificateHolder.getIssuer()));
              signatory.setCertificate(BouncyCastleProviderHelper.getCertificate(certificateHolder));
              signature.getSignatories().add(signatory);
            }
          }

          signatures.add(signature);
        }
      }

      return Collections.toArray(signatures, Signature.class);
    } catch (Exception e) {
      throw new SignerException(e);
    }
  }

  public byte[] wrap(final byte[] data, final byte[] signature) throws IOException {
    DERSequence derSequence = (DERSequence) BouncyCastleProviderHelper.toASN1Primitive(signature);
    ContentInfo signatureContentInfo = new ContentInfo(derSequence);
    SignedData signatureSignedData = SignedData.getInstance(signatureContentInfo.getContent());
    ContentInfo dataContentInfo = new ContentInfo(CMSObjectIdentifiers.data, new BEROctetString(data));
    SignedData dataSignedData = new SignedData(signatureSignedData.getDigestAlgorithms(), dataContentInfo, signatureSignedData.getCertificates(), signatureSignedData.getCRLs(), signatureSignedData.getSignerInfos());
    ContentInfo fullContentInfo = new ContentInfo(PKCSObjectIdentifiers.signedData, dataSignedData);
    return fullContentInfo.getEncoded();
  }

  public byte[] getContent(final byte[] data) {
    try {
      CMSSignedData signedData = new CMSSignedData(data);
      CMSProcessable processable = signedData.getSignedContent();
      return this.getContent(processable);
    } catch (Exception e) {
      throw new SignerException(e);
    }
  }

  private byte[] getContent(final CMSProcessable processable) {
    if (processable != null) {
      Object content = processable.getContent();
      if (content != null) {
        if (Arrays.isArrayOfType(content, byte.class)) {
          byte[] bytes = (byte[]) content;
          if (bytes.length > 0) {
            return bytes;
          }
          return null;
        }
        RockFrameworkLogger.getLogger().warn("Illegal content of type " + content.getClass().getCanonicalName());
      }
    }
    return null;
  }

  private DERSequence getAttribute(final AttributeTable signedAttributeTable, final AttributeTable unsignedAttributeTable, final ASN1ObjectIdentifier identifier) {
    DERSequence object = null;
    if ((signedAttributeTable != null) && (signedAttributeTable.get(identifier) != null)) {
      object = (DERSequence) signedAttributeTable.get(identifier).toASN1Primitive();
    } else if ((unsignedAttributeTable != null) && (unsignedAttributeTable.get(identifier) != null)) {
      object = (DERSequence) unsignedAttributeTable.get(identifier).toASN1Primitive();
    }
    return object;
  }

}
TOP

Related Classes of br.net.woodstock.rockframework.security.sign.impl.BouncyCastlePKCS7Signer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.