decrypt method
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');
}
}