/**
   * Return a signer information object with passed in SignerInformationStore representing counter
   * signatures attached as an unsigned attribute.
   *
   * @param signerInformation the signerInfo to be used as the basis.
   * @param counterSigners signer info objects carrying counter signature.
   * @return a copy of the original SignerInformationObject with the changed attributes.
   */
  public static SignerInformation addCounterSigners(
      SignerInformation signerInformation, SignerInformationStore counterSigners) {
    // TODO Perform checks from RFC 3852 11.4

    SignerInfo sInfo = signerInformation.info;
    AttributeTable unsignedAttr = signerInformation.getUnsignedAttributes();
    ASN1EncodableVector v;

    if (unsignedAttr != null) {
      v = unsignedAttr.toASN1EncodableVector();
    } else {
      v = new ASN1EncodableVector();
    }

    ASN1EncodableVector sigs = new ASN1EncodableVector();

    for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext(); ) {
      sigs.add(((SignerInformation) it.next()).toSignerInfo());
    }

    v.add(new Attribute(CMSAttributes.counterSignature, new DERSet(sigs)));

    return new SignerInformation(
        new SignerInfo(
            sInfo.getSID(),
            sInfo.getDigestAlgorithm(),
            sInfo.getAuthenticatedAttributes(),
            sInfo.getDigestEncryptionAlgorithm(),
            sInfo.getEncryptedDigest(),
            new DERSet(v)),
        signerInformation.contentType,
        signerInformation.content,
        null,
        new DefaultSignatureAlgorithmIdentifierFinder());
  }
  SignerInformation(
      SignerInfo info,
      ASN1ObjectIdentifier contentType,
      CMSProcessable content,
      IntDigestCalculator digestCalculator,
      SignatureAlgorithmIdentifierFinder sigAlgFinder) {
    this.info = info;
    this.contentType = contentType;
    this.sigAlgFinder = sigAlgFinder;
    this.isCounterSignature = contentType == null;

    SignerIdentifier s = info.getSID();

    if (s.isTagged()) {
      ASN1OctetString octs = ASN1OctetString.getInstance(s.getId());

      sid = new SignerId(octs.getOctets());
    } else {
      IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.getInstance(s.getId());

      sid = new SignerId(iAnds.getName(), iAnds.getSerialNumber().getValue());
    }

    this.digestAlgorithm = info.getDigestAlgorithm();
    this.signedAttributeSet = info.getAuthenticatedAttributes();
    this.unsignedAttributeSet = info.getUnauthenticatedAttributes();
    this.encryptionAlgorithm = info.getDigestEncryptionAlgorithm();
    this.signature = info.getEncryptedDigest().getOctets();

    this.content = content;
    this.digestCalculator = digestCalculator;
  }
  /**
   * Return a signer information object with the passed in unsigned attributes replacing the ones
   * that are current associated with the object passed in.
   *
   * @param signerInformation the signerInfo to be used as the basis.
   * @param unsignedAttributes the unsigned attributes to add.
   * @return a copy of the original SignerInformationObject with the changed attributes.
   */
  public static SignerInformation replaceUnsignedAttributes(
      SignerInformation signerInformation, AttributeTable unsignedAttributes) {
    SignerInfo sInfo = signerInformation.info;
    ASN1Set unsignedAttr = null;

    if (unsignedAttributes != null) {
      unsignedAttr = new DERSet(unsignedAttributes.toASN1EncodableVector());
    }

    return new SignerInformation(
        new SignerInfo(
            sInfo.getSID(),
            sInfo.getDigestAlgorithm(),
            sInfo.getAuthenticatedAttributes(),
            sInfo.getDigestEncryptionAlgorithm(),
            sInfo.getEncryptedDigest(),
            unsignedAttr),
        signerInformation.contentType,
        signerInformation.content,
        null,
        new DefaultSignatureAlgorithmIdentifierFinder());
  }
Example #4
0
  @Override
  protected void onDocumentSigned(byte[] byteArray) {
    try {
      InputStream inputStream = new ByteArrayInputStream(byteArray);

      PDDocument document = PDDocument.load(inputStream);
      List<PDSignature> signatures = document.getSignatureDictionaries();
      assertEquals(1, signatures.size());

      for (PDSignature pdSignature : signatures) {
        byte[] contents = pdSignature.getContents(byteArray);
        byte[] signedContent = pdSignature.getSignedContent(byteArray);

        logger.info("Byte range : " + Arrays.toString(pdSignature.getByteRange()));

        // IOUtils.write(contents, new FileOutputStream("sig.p7s"));

        ASN1InputStream asn1sInput = new ASN1InputStream(contents);
        ASN1Sequence asn1Seq = (ASN1Sequence) asn1sInput.readObject();

        logger.info("SEQ : " + asn1Seq.toString());

        ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(asn1Seq.getObjectAt(0));
        assertEquals(PKCSObjectIdentifiers.signedData, oid);

        SignedData signedData =
            SignedData.getInstance(DERTaggedObject.getInstance(asn1Seq.getObjectAt(1)).getObject());

        ASN1Set digestAlgorithmSet = signedData.getDigestAlgorithms();
        ASN1ObjectIdentifier oidDigestAlgo =
            ASN1ObjectIdentifier.getInstance(
                ASN1Sequence.getInstance(digestAlgorithmSet.getObjectAt(0)).getObjectAt(0));
        DigestAlgorithm digestAlgorithm = DigestAlgorithm.forOID(oidDigestAlgo.getId());
        logger.info("DIGEST ALGO : " + digestAlgorithm);

        ContentInfo encapContentInfo = signedData.getEncapContentInfo();
        ASN1ObjectIdentifier contentTypeOID = encapContentInfo.getContentType();
        logger.info("ENCAPSULATED CONTENT INFO TYPE : " + contentTypeOID);
        assertEquals(PKCSObjectIdentifiers.data, contentTypeOID);

        ASN1Encodable content = encapContentInfo.getContent();
        logger.info("ENCAPSULATED CONTENT INFO CONTENT : " + content);
        assertNull(content);

        List<X509Certificate> certificates = extractCertificates(signedData);

        ASN1Set signerInfosAsn1 = signedData.getSignerInfos();
        logger.info("SIGNER INFO ASN1 : " + signerInfosAsn1.toString());
        SignerInfo signedInfo =
            SignerInfo.getInstance(ASN1Sequence.getInstance(signerInfosAsn1.getObjectAt(0)));

        ASN1Set authenticatedAttributeSet = signedInfo.getAuthenticatedAttributes();
        logger.info("AUTHENTICATED ATTR : " + authenticatedAttributeSet);

        List<ASN1ObjectIdentifier> attributeOids = new ArrayList<ASN1ObjectIdentifier>();
        for (int i = 0; i < authenticatedAttributeSet.size(); i++) {
          Attribute attribute = Attribute.getInstance(authenticatedAttributeSet.getObjectAt(i));
          attributeOids.add(attribute.getAttrType());
        }
        logger.info("List of OID for Auth Attrb : " + attributeOids);

        Attribute attributeDigest = Attribute.getInstance(authenticatedAttributeSet.getObjectAt(1));
        assertEquals(PKCSObjectIdentifiers.pkcs_9_at_messageDigest, attributeDigest.getAttrType());

        ASN1OctetString asn1ObjString =
            ASN1OctetString.getInstance(attributeDigest.getAttrValues().getObjectAt(0));
        String embeddedDigest = Base64.encode(asn1ObjString.getOctets());
        logger.info("MESSAGE DIGEST : " + embeddedDigest);

        byte[] digestSignedContent = DSSUtils.digest(digestAlgorithm, signedContent);
        String computedDigestSignedContentEncodeBase64 = Base64.encode(digestSignedContent);
        logger.info(
            "COMPUTED DIGEST SIGNED CONTENT BASE64 : " + computedDigestSignedContentEncodeBase64);
        assertEquals(embeddedDigest, computedDigestSignedContentEncodeBase64);

        SignerIdentifier sid = signedInfo.getSID();
        logger.info("SIGNER IDENTIFIER : " + sid.getId());

        IssuerAndSerialNumber issuerAndSerialNumber =
            IssuerAndSerialNumber.getInstance(signedInfo.getSID());
        ASN1Integer signerSerialNumber = issuerAndSerialNumber.getSerialNumber();
        logger.info(
            "ISSUER AND SN : " + issuerAndSerialNumber.getName() + " " + signerSerialNumber);

        BigInteger serial = issuerAndSerialNumber.getSerialNumber().getValue();
        X509Certificate signerCertificate = null;
        for (X509Certificate x509Certificate : certificates) {
          if (serial.equals(x509Certificate.getSerialNumber())) {
            signerCertificate = x509Certificate;
          }
        }
        assertNotNull(signerCertificate);

        String algorithm = signerCertificate.getPublicKey().getAlgorithm();
        EncryptionAlgorithm encryptionAlgorithm = EncryptionAlgorithm.forName(algorithm);

        ASN1OctetString encryptedInfoOctedString = signedInfo.getEncryptedDigest();
        String signatureValue = Hex.toHexString(encryptedInfoOctedString.getOctets());

        logger.info("SIGNATURE VALUE : " + signatureValue);

        Cipher cipher = Cipher.getInstance(encryptionAlgorithm.getName());
        cipher.init(Cipher.DECRYPT_MODE, signerCertificate);
        byte[] decrypted = cipher.doFinal(encryptedInfoOctedString.getOctets());

        ASN1InputStream inputDecrypted = new ASN1InputStream(decrypted);

        ASN1Sequence seqDecrypt = (ASN1Sequence) inputDecrypted.readObject();
        logger.info("DECRYPTED : " + seqDecrypt);

        DigestInfo digestInfo = new DigestInfo(seqDecrypt);
        assertEquals(oidDigestAlgo, digestInfo.getAlgorithmId().getAlgorithm());

        String decryptedDigestEncodeBase64 = Base64.encode(digestInfo.getDigest());
        logger.info("DECRYPTED BASE64 : " + decryptedDigestEncodeBase64);

        byte[] encoded = authenticatedAttributeSet.getEncoded();
        byte[] digest = DSSUtils.digest(digestAlgorithm, encoded);
        String computedDigestFromSignatureEncodeBase64 = Base64.encode(digest);
        logger.info(
            "COMPUTED DIGEST FROM SIGNATURE BASE64 : " + computedDigestFromSignatureEncodeBase64);

        assertEquals(decryptedDigestEncodeBase64, computedDigestFromSignatureEncodeBase64);

        IOUtils.closeQuietly(inputDecrypted);
        IOUtils.closeQuietly(asn1sInput);
      }

      IOUtils.closeQuietly(inputStream);
      document.close();
    } catch (Exception e) {
      logger.error(e.getMessage(), e);
      fail(e.getMessage());
    }
  }
  /**
   * Return a SignerInformationStore containing the counter signatures attached to this signer. If
   * no counter signatures are present an empty store is returned.
   */
  public SignerInformationStore getCounterSignatures() {
    // TODO There are several checks implied by the RFC3852 comments that are missing

    /*
    The countersignature attribute MUST be an unsigned attribute; it MUST
    NOT be a signed attribute, an authenticated attribute, an
    unauthenticated attribute, or an unprotected attribute.
    */
    AttributeTable unsignedAttributeTable = getUnsignedAttributes();
    if (unsignedAttributeTable == null) {
      return new SignerInformationStore(new ArrayList(0));
    }

    List counterSignatures = new ArrayList();

    /*
    The UnsignedAttributes syntax is defined as a SET OF Attributes.  The
    UnsignedAttributes in a signerInfo may include multiple instances of
    the countersignature attribute.
    */
    ASN1EncodableVector allCSAttrs = unsignedAttributeTable.getAll(CMSAttributes.counterSignature);

    for (int i = 0; i < allCSAttrs.size(); ++i) {
      Attribute counterSignatureAttribute = (Attribute) allCSAttrs.get(i);

      /*
      A countersignature attribute can have multiple attribute values.  The
      syntax is defined as a SET OF AttributeValue, and there MUST be one
      or more instances of AttributeValue present.
      */
      ASN1Set values = counterSignatureAttribute.getAttrValues();
      if (values.size() < 1) {
        // TODO Throw an appropriate exception?
      }

      for (Enumeration en = values.getObjects(); en.hasMoreElements(); ) {
        /*
        Countersignature values have the same meaning as SignerInfo values
        for ordinary signatures, except that:

           1. The signedAttributes field MUST NOT contain a content-type
              attribute; there is no content type for countersignatures.

           2. The signedAttributes field MUST contain a message-digest
              attribute if it contains any other attributes.

           3. The input to the message-digesting process is the contents
              octets of the DER encoding of the signatureValue field of the
              SignerInfo value with which the attribute is associated.
        */
        SignerInfo si = SignerInfo.getInstance(en.nextElement());

        String digestName =
            CMSSignedHelper.INSTANCE.getDigestAlgName(
                si.getDigestAlgorithm().getObjectId().getId());

        counterSignatures.add(
            new SignerInformation(
                si,
                null,
                null,
                new CounterSignatureDigestCalculator(digestName, null, getSignature()),
                new DefaultSignatureAlgorithmIdentifierFinder()));
      }
    }

    return new SignerInformationStore(counterSignatures);
  }
 /** return the version number for this objects underlying SignerInfo structure. */
 public int getVersion() {
   return info.getVersion().getValue().intValue();
 }