decrypt method

  1. @override
void decrypt(
  1. String password
)
override

Decrypts this entry using the given password.

Implementation

@override
void decrypt(String password) {
  if (isDecrypted()) return;

  try {
    if (encryptedDataBytes == null) {
      throw DecryptionFailureException('No encrypted data');
    }

    // Check if it's JCEKS (DER Sequence 0x30) or JKS (Proprietary)
    if (encryptedDataBytes!.isNotEmpty && encryptedDataBytes![0] == 0x30) {
      // JCEKS (EncryptedPrivateKeyInfo)
      final plaintext = _jceksDecrypt(encryptedDataBytes!, password);

      // JCEKS stores a parsed PrivateKeyInfo (PKCS#8) directly as plaintext?
      // Or EncryptedPrivateKeyInfo decrypts TO PrivateKeyInfo?
      // Yes, decrypting EncryptedPrivateKeyInfo gives PrivateKeyInfo.

      // PrivateKeyInfo structure (PKCS#8):
      // Sequence
      //   Version (0)
      //   AlgorithmIdentifier
      //   PrivateKey (OctetString)
      //   [Attributes]

      // We need to parse this to get the raw private key (for rawPrivateKey)
      // or just store the whole thing as pkcs8PrivateKey.
      pkcs8PrivateKey = plaintext;

      // Extract raw key?
      // Let's crudely extract it or leave it.
      // User wants "rawPrivateKey" usually for BouncyCastle.
      // But PrivateKeyEntry has pkcs8PrivateKey.
      // rawPrivateKey is usually valid if we can parse it.
      // For now, let's try to extract the OctetString if possible using our DER reader.
      try {
        final reader = _DerReader(plaintext);
        reader.readSequence(); // Top sequence
        final v = reader.readInt(); // Version
        if (v == 0) {
          reader.readSequence(); // AlgorithmIdentifier
          final octet = reader.readOctetString(); // PrivateKey
          rawPrivateKey = octet;
        }
      } catch (_) {
        // If parsing fails, just leave rawPrivateKey null or same as pkcs8
        rawPrivateKey = plaintext;
      }
    } else {
      // JKS (Legacy JDKKeyProtector)
      if (encryptedDataBytes!.length < 40) {
        throw DecryptionFailureException('Invalid JKS key data length');
      }

      final salt = encryptedDataBytes!.sublist(0, 20);
      final ciphertext =
          encryptedDataBytes!.sublist(20, encryptedDataBytes!.length - 20);
      final storedDigest =
          encryptedDataBytes!.sublist(encryptedDataBytes!.length - 20);

      // Decrypt
      final plaintext = _jksDecrypt(ciphertext, password, salt);

      // Verify integrity: SHA1(passwordBE || plaintext)
      final passwordBE = utf16BeEncode(password);
      final input = Uint8List(passwordBE.length + plaintext.length);
      input.setAll(0, passwordBE);
      input.setAll(passwordBE.length, plaintext);

      final computedDigest = _pkiCrypto.sha1Sync(input);

      if (!fixedTimeEqual(computedDigest, storedDigest)) {
        throw DecryptionFailureException(
            'Password incorrect or integrity check failed');
      }

      // Success
      // JKS proprietary format decrypts to the raw private key (PKCS#8 encoded usually)
      rawPrivateKey = plaintext;
      pkcs8PrivateKey = plaintext;
    }

    encryptedDataBytes = null;
  } catch (e) {
    if (e is KeystoreException) rethrow;
    throw DecryptionFailureException('Decryption failed: $e');
  }
}