/**
   * Generate a signature for the message we've been loaded with using the key we were initialised
   * with.
   */
  public byte[] generateSignature() throws CryptoException, DataLengthException {
    if (!forSigning) {
      throw new IllegalStateException("RSADigestSigner not initialised for signature generation.");
    }

    byte[] hash = new byte[digest.getDigestSize()];
    digest.doFinal(hash, 0);

    try {
      byte[] data = derEncode(hash);
      return rsaEngine.processBlock(data, 0, data.length);
    } catch (IOException e) {
      throw new CryptoException("unable to encode signature: " + e.getMessage(), e);
    }
  }
  /**
   * return true if the internal state represents the signature described in the passed in array.
   */
  public boolean verifySignature(byte[] signature) {
    if (forSigning) {
      throw new IllegalStateException("RSADigestSigner not initialised for verification");
    }

    byte[] hash = new byte[digest.getDigestSize()];

    digest.doFinal(hash, 0);

    byte[] sig;
    byte[] expected;

    try {
      sig = rsaEngine.processBlock(signature, 0, signature.length);
      expected = derEncode(hash);
    } catch (Exception e) {
      return false;
    }

    if (sig.length == expected.length) {
      return Arrays.constantTimeAreEqual(sig, expected);
    } else if (sig.length == expected.length - 2) // NULL left out
    {
      int sigOffset = sig.length - hash.length - 2;
      int expectedOffset = expected.length - hash.length - 2;

      expected[1] -= 2; // adjust lengths
      expected[3] -= 2;

      int nonEqual = 0;

      for (int i = 0; i < hash.length; i++) {
        nonEqual |= (sig[sigOffset + i] ^ expected[expectedOffset + i]);
      }

      for (int i = 0; i < sigOffset; i++) {
        nonEqual |= (sig[i] ^ expected[i]); // check header less NULL
      }

      return nonEqual == 0;
    } else {
      return false;
    }
  }
  /**
   * initialise the signer for signing or verification.
   *
   * @param forSigning true if for signing, false otherwise
   * @param parameters necessary parameters.
   */
  public void init(boolean forSigning, CipherParameters parameters) {
    this.forSigning = forSigning;
    AsymmetricKeyParameter k;

    if (parameters instanceof ParametersWithRandom) {
      k = (AsymmetricKeyParameter) ((ParametersWithRandom) parameters).getParameters();
    } else {
      k = (AsymmetricKeyParameter) parameters;
    }

    if (forSigning && !k.isPrivate()) {
      throw new IllegalArgumentException("signing requires private key");
    }

    if (!forSigning && k.isPrivate()) {
      throw new IllegalArgumentException("verification requires public key");
    }

    reset();

    rsaEngine.init(forSigning, parameters);
  }