@Override
  protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
      throws IllegalBlockSizeException, BadPaddingException {
    if (input != null) {
      engineUpdate(input, inputOffset, inputLen);
    }

    if (inputTooLarge) {
      throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes");
    }

    final byte[] tmpBuf;
    if (bufferOffset != buffer.length) {
      if (padding == NativeCrypto.RSA_NO_PADDING) {
        tmpBuf = new byte[buffer.length];
        System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset);
      } else {
        tmpBuf = Arrays.copyOf(buffer, bufferOffset);
      }
    } else {
      tmpBuf = buffer;
    }

    byte[] output = new byte[buffer.length];
    int resultSize;
    if (encrypting) {
      if (usingPrivateKey) {
        resultSize =
            NativeCrypto.RSA_private_encrypt(
                tmpBuf.length, tmpBuf, output, key.getPkeyContext(), padding);
      } else {
        resultSize =
            NativeCrypto.RSA_public_encrypt(
                tmpBuf.length, tmpBuf, output, key.getPkeyContext(), padding);
      }
    } else {
      try {
        if (usingPrivateKey) {
          resultSize =
              NativeCrypto.RSA_private_decrypt(
                  tmpBuf.length, tmpBuf, output, key.getPkeyContext(), padding);
        } else {
          resultSize =
              NativeCrypto.RSA_public_decrypt(
                  tmpBuf.length, tmpBuf, output, key.getPkeyContext(), padding);
        }
      } catch (SignatureException e) {
        IllegalBlockSizeException newE = new IllegalBlockSizeException();
        newE.initCause(e);
        throw newE;
      }
    }
    if (!encrypting && resultSize != output.length) {
      output = Arrays.copyOf(output, resultSize);
    }

    bufferOffset = 0;
    return output;
  }
  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
    stream.defaultReadObject();

    byte[] encoded = (byte[]) stream.readObject();

    key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));

    final long origGroup = NativeCrypto.EC_KEY_get0_group(key.getPkeyContext());
    group = new OpenSSLECGroupContext(NativeCrypto.EC_GROUP_dup(origGroup));
  }
 public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
   try {
     OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams());
     final BigInteger privKey = ecPrivateKey.getS();
     return new OpenSSLKey(
         NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(), 0, privKey.toByteArray()));
   } catch (Exception e) {
     throw new InvalidKeyException(e);
   }
 }
  @Override
  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }

    if (o instanceof OpenSSLRSAPrivateKey) {
      OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;

      /*
       * We can shortcut the true case, but it still may be equivalent but
       * different copies.
       */
      if (getOpenSSLKey().equals(other.getOpenSSLKey())) {
        return true;
      }

      return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1;
    }

    if (o instanceof RSAPrivateCrtKey) {
      ensureReadParams();
      RSAPrivateCrtKey other = (RSAPrivateCrtKey) o;

      if (getOpenSSLKey().isEngineBased()) {
        return getModulus().equals(other.getModulus())
            && publicExponent.equals(other.getPublicExponent());
      } else {
        return getModulus().equals(other.getModulus())
            && publicExponent.equals(other.getPublicExponent())
            && getPrivateExponent().equals(other.getPrivateExponent())
            && primeP.equals(other.getPrimeP())
            && primeQ.equals(other.getPrimeQ())
            && primeExponentP.equals(other.getPrimeExponentP())
            && primeExponentQ.equals(other.getPrimeExponentQ())
            && crtCoefficient.equals(other.getCrtCoefficient());
      }
    } else if (o instanceof RSAPrivateKey) {
      ensureReadParams();
      RSAPrivateKey other = (RSAPrivateKey) o;

      if (getOpenSSLKey().isEngineBased()) {
        return getModulus().equals(other.getModulus());
      } else {
        return getModulus().equals(other.getModulus())
            && getPrivateExponent().equals(other.getPrivateExponent());
      }
    }

    return false;
  }
  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
    stream.defaultReadObject();

    key =
        new OpenSSLKey(
            NativeCrypto.EVP_PKEY_new_RSA(
                modulus.toByteArray(),
                publicExponent == null ? null : publicExponent.toByteArray(),
                privateExponent.toByteArray(),
                primeP == null ? null : primeP.toByteArray(),
                primeQ == null ? null : primeQ.toByteArray(),
                primeExponentP == null ? null : primeExponentP.toByteArray(),
                primeExponentQ == null ? null : primeExponentQ.toByteArray(),
                crtCoefficient == null ? null : crtCoefficient.toByteArray()));
    fetchedParams = true;
  }
  static OpenSSLKey getInstance(RSAPrivateCrtKey rsaPrivateKey) throws InvalidKeyException {
    BigInteger modulus = rsaPrivateKey.getModulus();
    BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();

    if (modulus == null) {
      throw new InvalidKeyException("modulus == null");
    } else if (privateExponent == null) {
      throw new InvalidKeyException("privateExponent == null");
    }

    try {
      /*
       * OpenSSL uses the public modulus to do RSA blinding. If
       * the public modulus is not available, the call to
       * EVP_PKEY_new_RSA will turn off blinding for this key
       * instance.
       */
      final BigInteger publicExponent = rsaPrivateKey.getPublicExponent();
      final BigInteger primeP = rsaPrivateKey.getPrimeP();
      final BigInteger primeQ = rsaPrivateKey.getPrimeQ();
      final BigInteger primeExponentP = rsaPrivateKey.getPrimeExponentP();
      final BigInteger primeExponentQ = rsaPrivateKey.getPrimeExponentQ();
      final BigInteger crtCoefficient = rsaPrivateKey.getCrtCoefficient();

      return new OpenSSLKey(
          NativeCrypto.EVP_PKEY_new_RSA(
              modulus.toByteArray(),
              publicExponent == null ? null : publicExponent.toByteArray(),
              privateExponent.toByteArray(),
              primeP == null ? null : primeP.toByteArray(),
              primeQ == null ? null : primeQ.toByteArray(),
              primeExponentP == null ? null : primeExponentP.toByteArray(),
              primeExponentQ == null ? null : primeExponentQ.toByteArray(),
              crtCoefficient == null ? null : crtCoefficient.toByteArray()));
    } catch (Exception e) {
      throw new InvalidKeyException(e);
    }
  }
  private void engineInitInternal(int opmode, Key key) throws InvalidKeyException {
    if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
      encrypting = true;
    } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
      encrypting = false;
    } else {
      throw new InvalidParameterException("Unsupported opmode " + opmode);
    }

    if (key instanceof OpenSSLRSAPrivateKey) {
      OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) key;
      usingPrivateKey = true;
      this.key = rsaPrivateKey.getOpenSSLKey();
    } else if (key instanceof RSAPrivateCrtKey) {
      RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) key;
      usingPrivateKey = true;
      this.key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
    } else if (key instanceof RSAPrivateKey) {
      RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) key;
      usingPrivateKey = true;
      this.key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
    } else if (key instanceof OpenSSLRSAPublicKey) {
      OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) key;
      usingPrivateKey = false;
      this.key = rsaPublicKey.getOpenSSLKey();
    } else if (key instanceof RSAPublicKey) {
      RSAPublicKey rsaPublicKey = (RSAPublicKey) key;
      usingPrivateKey = false;
      this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
    } else {
      throw new InvalidKeyException("Need RSA private or public key");
    }

    buffer = new byte[NativeCrypto.RSA_size(this.key.getPkeyContext())];
    inputTooLarge = false;
  }
 @Override
 public byte[] getEncoded() {
   return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
 }
 public OpenSSLECPrivateKey(OpenSSLKey key) {
   final long origGroup = NativeCrypto.EC_KEY_get0_group(key.getPkeyContext());
   this.group = new OpenSSLECGroupContext(NativeCrypto.EC_GROUP_dup(origGroup));
   this.key = key;
 }
 @Override
 public String toString() {
   return NativeCrypto.EVP_PKEY_print_private(key.getPkeyContext());
 }
 @Override
 public int hashCode() {
   return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext()));
 }
 private BigInteger getPrivateKey() {
   return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getPkeyContext()));
 }