/**
   * 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());
  }