public static PKCS10CertificationRequest genPKCS10(KeyPair kp) throws Exception {
    String sigName = "SHA1withRSA";

    X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE);
    x500NameBld.addRDN(BCStyle.C, "AU");
    x500NameBld.addRDN(BCStyle.ST, "Victoria");
    x500NameBld.addRDN(BCStyle.L, "Melbourne");
    x500NameBld.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
    X500Name subject = x500NameBld.build();

    PKCS10CertificationRequestBuilder requestBuilder =
        new JcaPKCS10CertificationRequestBuilder(subject, kp.getPublic());

    ExtensionsGenerator extGen = new ExtensionsGenerator();
    extGen.addExtension(
        Extension.subjectAlternativeName,
        false,
        new GeneralNames(
            new GeneralName(GeneralName.rfc822Name, "*****@*****.**")));

    requestBuilder.addAttribute(
        PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());

    PKCS10CertificationRequest p10 =
        requestBuilder.build(
            new JcaContentSignerBuilder(sigName).setProvider("BC").build(kp.getPrivate()));

    if (!p10.isSignatureValid(
        new JcaContentVerifierProviderBuilder().setProvider("BC").build(kp.getPublic()))) {
      System.out.println(sigName + ": Failed verify check.");
    } else {
      System.out.println(sigName + ": PKCS#10 request verified.");
    }
    return p10;
  }
  /**
   * Generate a TimeStampToken for the passed in request and serialNumber marking it with the passed
   * in genTime.
   *
   * @param request the originating request.
   * @param serialNumber serial number for the TimeStampToken
   * @param genTime token generation time.
   * @param additionalExtensions extra extensions to be added to the response token.
   * @return a TimeStampToken
   * @throws TSPException
   */
  public TimeStampToken generate(
      TimeStampRequest request,
      BigInteger serialNumber,
      Date genTime,
      Extensions additionalExtensions)
      throws TSPException {
    ASN1ObjectIdentifier digestAlgOID = request.getMessageImprintAlgOID();

    AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DERNull.INSTANCE);
    MessageImprint messageImprint = new MessageImprint(algID, request.getMessageImprintDigest());

    Accuracy accuracy = null;
    if (accuracySeconds > 0 || accuracyMillis > 0 || accuracyMicros > 0) {
      ASN1Integer seconds = null;
      if (accuracySeconds > 0) {
        seconds = new ASN1Integer(accuracySeconds);
      }

      ASN1Integer millis = null;
      if (accuracyMillis > 0) {
        millis = new ASN1Integer(accuracyMillis);
      }

      ASN1Integer micros = null;
      if (accuracyMicros > 0) {
        micros = new ASN1Integer(accuracyMicros);
      }

      accuracy = new Accuracy(seconds, millis, micros);
    }

    ASN1Boolean derOrdering = null;
    if (ordering) {
      derOrdering = ASN1Boolean.getInstance(ordering);
    }

    ASN1Integer nonce = null;
    if (request.getNonce() != null) {
      nonce = new ASN1Integer(request.getNonce());
    }

    ASN1ObjectIdentifier tsaPolicy = tsaPolicyOID;
    if (request.getReqPolicy() != null) {
      tsaPolicy = request.getReqPolicy();
    }

    Extensions respExtensions = request.getExtensions();
    if (additionalExtensions != null) {
      ExtensionsGenerator extGen = new ExtensionsGenerator();

      if (respExtensions != null) {
        for (Enumeration en = respExtensions.oids(); en.hasMoreElements(); ) {
          extGen.addExtension(
              respExtensions.getExtension(ASN1ObjectIdentifier.getInstance(en.nextElement())));
        }
      }
      for (Enumeration en = additionalExtensions.oids(); en.hasMoreElements(); ) {
        extGen.addExtension(
            additionalExtensions.getExtension(ASN1ObjectIdentifier.getInstance(en.nextElement())));
      }

      respExtensions = extGen.generate();
    }

    TSTInfo tstInfo =
        new TSTInfo(
            tsaPolicy,
            messageImprint,
            new ASN1Integer(serialNumber),
            new ASN1GeneralizedTime(genTime),
            accuracy,
            derOrdering,
            nonce,
            tsa,
            respExtensions);

    try {
      CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator();

      if (request.getCertReq()) {
        // TODO: do we need to check certs non-empty?
        signedDataGenerator.addCertificates(new CollectionStore(certs));
        signedDataGenerator.addAttributeCertificates(new CollectionStore(attrCerts));
      }

      signedDataGenerator.addCRLs(new CollectionStore(crls));

      if (!otherRevoc.isEmpty()) {
        for (Iterator it = otherRevoc.keySet().iterator(); it.hasNext(); ) {
          ASN1ObjectIdentifier format = (ASN1ObjectIdentifier) it.next();

          signedDataGenerator.addOtherRevocationInfo(
              format, new CollectionStore((Collection) otherRevoc.get(format)));
        }
      }

      signedDataGenerator.addSignerInfoGenerator(signerInfoGen);

      byte[] derEncodedTSTInfo = tstInfo.getEncoded(ASN1Encoding.DER);

      CMSSignedData signedData =
          signedDataGenerator.generate(
              new CMSProcessableByteArray(PKCSObjectIdentifiers.id_ct_TSTInfo, derEncodedTSTInfo),
              true);

      return new TimeStampToken(signedData);
    } catch (CMSException cmsEx) {
      throw new TSPException("Error generating time-stamp token", cmsEx);
    } catch (IOException e) {
      throw new TSPException("Exception encoding info", e);
    }
  }