/**
   * Generate a CMS (Cryptographic Message Syntax) signature for a given byte content. The resulting
   * signature might contains the content itself and the certificate chain of the key used to sign.
   *
   * @param data the data to be signed
   * @param keyPair the certified key pair used for signing
   * @param certificateProvider Optionally, a certificate provider for obtaining the chain of
   *     certificate to embed. If null, no certificate are embedded with the signature.
   * @param existingSignature if not null, a existing signature on the same data that should be
   *     kept.
   * @param embedContent if true, the signed content is embedded with the signature.
   * @return the resulting signature encoded ASN.1 and in accordance with RFC 3852.
   * @throws GeneralSecurityException on error.
   */
  public byte[] cmsSign(
      byte[] data,
      CertifiedKeyPair keyPair,
      CertificateProvider certificateProvider,
      CMSSignedDataVerified existingSignature,
      boolean embedContent)
      throws GeneralSecurityException {
    CMSSignedDataGeneratorParameters parameters =
        new CMSSignedDataGeneratorParameters()
            .addSigner(CertifyingSigner.getInstance(true, keyPair, signerFactory));

    if (existingSignature != null) {
      for (CMSSignerInfo existingSigner : existingSignature.getSignatures()) {
        parameters.addSignature(existingSigner);
      }
    }

    Set<CertifiedPublicKey> certs = new HashSet<CertifiedPublicKey>();
    if (existingSignature != null && existingSignature.getCertificates() != null) {
      certs.addAll(existingSignature.getCertificates());
    }

    if (certificateProvider != null) {
      if (existingSignature != null) {
        for (CMSSignerInfo existingSigner : existingSignature.getSignatures()) {
          if (existingSigner.getSubjectKeyIdentifier() != null) {
            addCertificateChain(
                certificateProvider.getCertificate(existingSigner.getSubjectKeyIdentifier()),
                certificateProvider,
                certs);
          } else {
            addCertificateChain(
                certificateProvider.getCertificate(
                    existingSigner.getIssuer(), existingSigner.getSerialNumber()),
                certificateProvider,
                certs);
          }
        }
      }

      addCertificateChain(keyPair.getCertificate(), certificateProvider, certs);
    }

    if (!certs.isEmpty()) {
      parameters.addCertificates(certs);
    }

    return cmsSignedDataGenerator.generate(data, parameters, embedContent);
  }
 /**
  * Create an intermediate CA certificate.
  *
  * @param issuer the certified keypair for issuing the certificate
  * @param publicKey the public key to certify
  * @param dn the distinguished name for the new the certificate.
  * @param validity the validity of the certificate from now in days.
  * @return a certified public key.
  * @throws IOException in case on error while reading the public key.
  * @throws GeneralSecurityException in case of error.
  */
 public CertifiedPublicKey issueIntermediateCertificate(
     CertifiedKeyPair issuer, PublicKeyParameters publicKey, String dn, int validity)
     throws IOException, GeneralSecurityException {
   return certificateGeneratorFactory
       .getInstance(
           CertifyingSigner.getInstance(true, issuer, signerFactory),
           new X509CertificateGenerationParameters(
               validity,
               extensionBuilder
                   .get()
                   .addBasicConstraints(0)
                   .addKeyUsage(EnumSet.of(KeyUsage.keyCertSign, KeyUsage.cRLSign))
                   .build()))
       .generate(new DistinguishedName(dn), publicKey, new X509CertificateParameters());
 }
  /**
   * Create an end entity certificate. By default, the key can be used for encryption and signing.
   * If the end entity contains some alternate subject names of type X509Rfc822Name a extended email
   * protection usage is added. If the end entity contains some alternate subject names of type
   * X509DnsName or X509IpAddress extended server and client authentication usages are added.
   *
   * @param issuer the keypair for issuing the certificate
   * @param publicKey the public key to certify
   * @param dn the distinguished name for the new the certificate.
   * @param validity the validity of the certificate from now in days.
   * @param subjectAltName the alternative names for the certificate
   * @return a certified public key.
   * @throws IOException in case on error while reading the public key.
   * @throws GeneralSecurityException in case of error.
   */
  public CertifiedPublicKey issueCertificate(
      CertifiedKeyPair issuer,
      PublicKeyParameters publicKey,
      String dn,
      int validity,
      List<X509GeneralName> subjectAltName)
      throws IOException, GeneralSecurityException {
    X509CertificateParameters params;
    X509ExtensionBuilder builder =
        extensionBuilder
            .get()
            .addKeyUsage(EnumSet.of(KeyUsage.digitalSignature, KeyUsage.dataEncipherment));

    if (subjectAltName != null) {
      params =
          new X509CertificateParameters(
              extensionBuilder
                  .get()
                  .addSubjectAltName(false, subjectAltName.toArray(new X509GeneralName[] {}))
                  .build());
      Set<String> extUsage = new HashSet<String>();
      for (X509GeneralName genName : subjectAltName) {
        if (genName instanceof X509Rfc822Name) {
          extUsage.add(ExtendedKeyUsages.EMAIL_PROTECTION);
        } else if (genName instanceof X509DnsName || genName instanceof X509IpAddress) {
          extUsage.add(ExtendedKeyUsages.SERVER_AUTH);
          extUsage.add(ExtendedKeyUsages.CLIENT_AUTH);
        }
        builder.addExtendedKeyUsage(false, new ExtendedKeyUsages(extUsage));
      }
    } else {
      params = new X509CertificateParameters();
    }

    return certificateGeneratorFactory
        .getInstance(
            CertifyingSigner.getInstance(true, issuer, signerFactory),
            new X509CertificateGenerationParameters(validity, builder.build()))
        .generate(new DistinguishedName(dn), publicKey, params);
  }