/**
   * Return a copy of the passed in secret key, encrypted using a new password and the passed in
   * algorithm.
   *
   * @param key the PGPSecretKey to be copied.
   * @param oldKeyDecryptor the current decryptor based on the current password for key.
   * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key
   *     material.
   */
  public static PGPSecretKey copyWithNewPassword(
      PGPSecretKey key,
      PBESecretKeyDecryptor oldKeyDecryptor,
      PBESecretKeyEncryptor newKeyEncryptor)
      throws PGPException {
    if (key.isPrivateKeyEmpty()) {
      throw new PGPException("no private key in this SecretKey - public key present only.");
    }

    byte[] rawKeyData = key.extractKeyData(oldKeyDecryptor);
    int s2kUsage = key.secret.getS2KUsage();
    byte[] iv = null;
    S2K s2k = null;
    byte[] keyData;
    int newEncAlgorithm = SymmetricKeyAlgorithmTags.NULL;

    if (newKeyEncryptor == null
        || newKeyEncryptor.getAlgorithm() == SymmetricKeyAlgorithmTags.NULL) {
      s2kUsage = SecretKeyPacket.USAGE_NONE;
      if (key.secret.getS2KUsage()
          == SecretKeyPacket.USAGE_SHA1) // SHA-1 hash, need to rewrite checksum
      {
        keyData = new byte[rawKeyData.length - 18];

        System.arraycopy(rawKeyData, 0, keyData, 0, keyData.length - 2);

        byte[] check = checksum(null, keyData, keyData.length - 2);

        keyData[keyData.length - 2] = check[0];
        keyData[keyData.length - 1] = check[1];
      } else {
        keyData = rawKeyData;
      }
    } else {
      if (key.secret.getPublicKeyPacket().getVersion() < 4) {
        // Version 2 or 3 - RSA Keys only

        byte[] encKey = newKeyEncryptor.getKey();
        keyData = new byte[rawKeyData.length];

        if (newKeyEncryptor.getS2K() != null) {
          throw new PGPException("MD5 Digest Calculator required for version 3 key encryptor.");
        }

        //
        // process 4 numbers
        //
        int pos = 0;
        for (int i = 0; i != 4; i++) {
          int encLen = (((rawKeyData[pos] << 8) | (rawKeyData[pos + 1] & 0xff)) + 7) / 8;

          keyData[pos] = rawKeyData[pos];
          keyData[pos + 1] = rawKeyData[pos + 1];

          byte[] tmp;
          if (i == 0) {
            tmp = newKeyEncryptor.encryptKeyData(encKey, rawKeyData, pos + 2, encLen);
            iv = newKeyEncryptor.getCipherIV();

          } else {
            byte[] tmpIv = new byte[iv.length];

            System.arraycopy(keyData, pos - iv.length, tmpIv, 0, tmpIv.length);
            tmp = newKeyEncryptor.encryptKeyData(encKey, tmpIv, rawKeyData, pos + 2, encLen);
          }

          System.arraycopy(tmp, 0, keyData, pos + 2, tmp.length);
          pos += 2 + encLen;
        }

        //
        // copy in checksum.
        //
        keyData[pos] = rawKeyData[pos];
        keyData[pos + 1] = rawKeyData[pos + 1];

        s2k = newKeyEncryptor.getS2K();
        newEncAlgorithm = newKeyEncryptor.getAlgorithm();
      } else {
        keyData = newKeyEncryptor.encryptKeyData(rawKeyData, 0, rawKeyData.length);

        iv = newKeyEncryptor.getCipherIV();

        s2k = newKeyEncryptor.getS2K();

        newEncAlgorithm = newKeyEncryptor.getAlgorithm();
      }
    }

    SecretKeyPacket secret;
    if (key.secret instanceof SecretSubkeyPacket) {
      secret =
          new SecretSubkeyPacket(
              key.secret.getPublicKeyPacket(), newEncAlgorithm, s2kUsage, s2k, iv, keyData);
    } else {
      secret =
          new SecretKeyPacket(
              key.secret.getPublicKeyPacket(), newEncAlgorithm, s2kUsage, s2k, iv, keyData);
    }

    return new PGPSecretKey(secret, key.pub);
  }
  PGPSecretKey(
      PGPPrivateKey privKey,
      PGPPublicKey pubKey,
      PGPDigestCalculator checksumCalculator,
      boolean isMasterKey,
      PBESecretKeyEncryptor keyEncryptor)
      throws PGPException {
    this.pub = pubKey;

    BCPGObject secKey = (BCPGObject) privKey.getPrivateKeyDataPacket();

    try {
      ByteArrayOutputStream bOut = new ByteArrayOutputStream();
      BCPGOutputStream pOut = new BCPGOutputStream(bOut);

      pOut.writeObject(secKey);

      byte[] keyData = bOut.toByteArray();

      pOut.write(checksum(checksumCalculator, keyData, keyData.length));

      int encAlgorithm = keyEncryptor.getAlgorithm();

      if (encAlgorithm != SymmetricKeyAlgorithmTags.NULL) {
        keyData = bOut.toByteArray(); // include checksum

        byte[] encData = keyEncryptor.encryptKeyData(keyData, 0, keyData.length);
        byte[] iv = keyEncryptor.getCipherIV();

        S2K s2k = keyEncryptor.getS2K();

        int s2kUsage;

        if (checksumCalculator != null) {
          if (checksumCalculator.getAlgorithm() != HashAlgorithmTags.SHA1) {
            throw new PGPException("only SHA1 supported for key checksum calculations.");
          }
          s2kUsage = SecretKeyPacket.USAGE_SHA1;
        } else {
          s2kUsage = SecretKeyPacket.USAGE_CHECKSUM;
        }

        if (isMasterKey) {
          this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
        } else {
          this.secret =
              new SecretSubkeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
        }
      } else {
        if (isMasterKey) {
          this.secret =
              new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray());
        } else {
          this.secret =
              new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray());
        }
      }
    } catch (PGPException e) {
      throw e;
    } catch (Exception e) {
      throw new PGPException("Exception encrypting key", e);
    }
  }