save method

Uint8List save(
  1. String storePassword, {
  2. String? keyPassword,
})

Saves the keystore to BKS format bytes (Version 2).

storePassword - The password to protect keystore integrity (HMAC). keyPassword - The password to seal private keys. If not provided, defaults to storePassword.

Implementation

Uint8List save(String storePassword, {String? keyPassword}) {
  final salt = _pkiCrypto.randomBytes(20);
  const iterationCount = 10000;
  final header = BytesBuilder();
  header.add(_int32ToBytes(2));
  header.add(_int32ToBytes(salt.length));
  header.add(salt);
  header.add(_int32ToBytes(iterationCount));

  final entriesData = BytesBuilder();
  void writeEntriesV2(BytesBuilder b) {
    for (final entry in entries.values) {
      if (entry is BksTrustedCertEntry || entry is TrustedCertEntry) {
        b.addByte(bksEntryTypeCertificate);
        _writeUtf(b, entry.alias);
        b.add(_int64ToBytes(entry.timestamp));
        b.add(_int32ToBytes(0)); // Chain len
        final c = entry as TrustedCertEntry;
        _writeUtf(b, c.certType);
        _writeData(b, c.certData);
      } else if (entry is PrivateKeyEntry) {
        final inner = BytesBuilder();
        inner.addByte(bksKeyTypePrivate);
        _writeUtf(inner, 'PKCS#8');
        _writeUtf(inner, "RSA");
        final pkcs8 = entry.pkcs8PrivateKey ?? entry.rawPrivateKey;
        if (pkcs8 == null) {
          throw KeystoreException("No private key for ${entry.alias}");
        }
        _writeData(inner, pkcs8);

        final kPwd = keyPassword ?? storePassword;
        final sealed = _seal(inner.toBytes(), kPwd);

        b.addByte(bksEntryTypeSealed);
        _writeUtf(b, entry.alias);
        b.add(_int64ToBytes(entry.timestamp));

        b.add(_int32ToBytes(entry.certChain.length));
        for (final c in entry.certChain) {
          _writeUtf(b, c.$1);
          _writeData(b, c.$2);
        }
        _writeData(b, sealed);
      } else if (entry is BksSealedKeyEntry) {
        if (!entry.isDecrypted()) {
          throw KeystoreException(
            "Cannot save undecrypted BksSealedKeyEntry ${entry.alias}",
          );
        }
        final nested = entry.nestedEntry;
        final inner = BytesBuilder();
        inner.addByte(nested.keyType);
        _writeUtf(inner, nested.format);
        _writeUtf(inner, nested.algorithm);
        _writeData(inner, nested.encoded);

        final kPwd = keyPassword ?? storePassword;
        final sealed = _seal(inner.toBytes(), kPwd);

        b.addByte(bksEntryTypeSealed);
        _writeUtf(b, entry.alias);
        b.add(_int64ToBytes(entry.timestamp));
        b.add(_int32ToBytes(nested.certChain.length));
        for (final c in nested.certChain) {
          _writeUtf(b, c.certType);
          _writeData(b, c.certData);
        }
        _writeData(b, sealed);
      }
    }
    b.addByte(0);
  }

  writeEntriesV2(entriesData);
  final entriesBytes = entriesData.toBytes();

  final hmacKey = BksKeyStore._derivePkcs12Key(
      storePassword, salt, iterationCount, 20, 3);
  final signatureBytes = _pkiCrypto.hmacSha1Sync(hmacKey, entriesBytes);

  final out = BytesBuilder();
  out.add(header.toBytes());
  out.add(entriesBytes);
  out.add(signatureBytes);
  return out.toBytes();
}