/// <summary> /// Fetches a known system key from metastore and decrypts it using the key management service. /// </summary> /// /// <returns>The decrypted system key.</returns> /// /// <param name="systemKeyMeta">The <see cref="KeyMeta"/> of the system key.</param> /// <exception cref="MetadataMissingException">If the system key is not found.</exception> internal virtual CryptoKey GetSystemKey(KeyMeta systemKeyMeta) { EnvelopeKeyRecord systemKeyRecord = LoadKeyRecord(systemKeyMeta.KeyId, systemKeyMeta.Created); return(keyManagementService.DecryptKey( systemKeyRecord.EncryptedKey, systemKeyRecord.Created, systemKeyRecord.Revoked.IfNone(false))); }
/// <inheritdoc/> public virtual byte[] DecryptDataRowRecord(JObject dataRowRecord) { using (MetricsUtil.MetricsInstance.Measure.Timer.Time(DecryptTimerOptions)) { Json dataRowRecordJson = new Json(dataRowRecord); Json keyDocument = dataRowRecordJson.GetJson("Key"); byte[] payloadEncrypted = dataRowRecordJson.GetBytes("Data"); EnvelopeKeyRecord dataRowKeyRecord = new EnvelopeKeyRecord(keyDocument); KeyMeta keyMeta = dataRowKeyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException("Could not find parentKeyMeta {IK} for dataRowKey")); if (!partition.IsValidIntermediateKeyId(keyMeta.KeyId)) { throw new MetadataMissingException("Could not find parentKeyMeta {IK} for dataRowKey"); } byte[] decryptedPayload = WithIntermediateKeyForRead( keyMeta, intermediateCryptoKey => crypto.EnvelopeDecrypt( payloadEncrypted, dataRowKeyRecord.EncryptedKey, dataRowKeyRecord.Created, intermediateCryptoKey)); return(decryptedPayload); } }
/// <summary> /// Fetches a known intermediate key from metastore and decrypts it using its associated system key. /// </summary> /// /// <returns>The decrypted intermediate key.</returns> /// /// <param name="intermediateKeyCreated">The creation time of intermediate key.</param> /// <exception cref="MetadataMissingException">If the intermediate key is not found, or it has missing system /// key info.</exception> internal virtual CryptoKey GetIntermediateKey(DateTimeOffset intermediateKeyCreated) { EnvelopeKeyRecord intermediateKeyRecord = LoadKeyRecord(partition.IntermediateKeyId, intermediateKeyCreated); return(WithExistingSystemKey( intermediateKeyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException("Could not find parentKeyMeta (SK) for intermediateKey")), false, key => DecryptKey(intermediateKeyRecord, key))); }
/// <inheritdoc/> public virtual JObject EncryptPayload(byte[] payload) { using (MetricsUtil.MetricsInstance.Measure.Timer.Time(EncryptTimerOptions)) { EnvelopeEncryptResult result = WithIntermediateKeyForWrite(intermediateCryptoKey => crypto.EnvelopeEncrypt( payload, intermediateCryptoKey, new KeyMeta(partition.IntermediateKeyId, intermediateCryptoKey.GetCreated()))); KeyMeta parentKeyMeta = (KeyMeta)result.UserState; EnvelopeKeyRecord keyRecord = new EnvelopeKeyRecord(DateTimeOffset.UtcNow, parentKeyMeta, result.EncryptedKey); Json wrapperDocument = new Json(); wrapperDocument.Put("Key", keyRecord.ToJson()); wrapperDocument.Put("Data", result.CipherText); return(wrapperDocument.ToJObject()); } }
internal virtual bool IsKeyExpiredOrRevoked(EnvelopeKeyRecord envelopeKeyRecord) { return(cryptoPolicy.IsKeyExpired(envelopeKeyRecord.Created) || envelopeKeyRecord.Revoked.IfNone(false)); }
/// <summary> /// Decrypts the <paramref name="keyRecord"/>'s encrypted key using the provided key. /// </summary> /// /// <returns>The decrypted key contained in the <paramref name="keyRecord"/>.</returns> /// /// <param name="keyRecord">The key to decrypt.</param> /// <param name="keyEncryptionKey">Encryption key to use for decryption.</param> internal virtual CryptoKey DecryptKey(EnvelopeKeyRecord keyRecord, CryptoKey keyEncryptionKey) { return(crypto.DecryptKey( keyRecord.EncryptedKey, keyRecord.Created, keyEncryptionKey, keyRecord.Revoked.IfNone(false))); }
internal virtual CryptoKey GetLatestOrCreateSystemKey() { Option <EnvelopeKeyRecord> newestSystemKeyRecord = LoadLatestKeyRecord(partition.SystemKeyId); if (newestSystemKeyRecord.IsSome) { EnvelopeKeyRecord keyRecord = (EnvelopeKeyRecord)newestSystemKeyRecord; // If the key we just got back isn't expired, then just use it if (!IsKeyExpiredOrRevoked(keyRecord)) { return(keyManagementService.DecryptKey( keyRecord.EncryptedKey, keyRecord.Created, keyRecord.Revoked.IfNone(false))); } // If we're here we know we have an expired key. // If we're doing queued rotation, flag it and continue to use the expired key if (cryptoPolicy.IsQueuedKeyRotation()) { // TODO : Queued rotation Logger.LogDebug("Queuing up SK {keyId} for rotation", partition.SystemKeyId); return(keyManagementService.DecryptKey( keyRecord.EncryptedKey, keyRecord.Created, keyRecord.Revoked.IfNone(false))); } // If we're here then we're doing inline rotation and have an expired key. // Fall through as if we didn't have the key } DateTimeOffset systemKeyCreated = cryptoPolicy.TruncateToSystemKeyPrecision(DateTimeOffset.UtcNow); CryptoKey systemKey = crypto.GenerateKey(systemKeyCreated); try { EnvelopeKeyRecord newSystemKeyRecord = new EnvelopeKeyRecord( systemKey.GetCreated(), null, keyManagementService.EncryptKey(systemKey), false); Logger.LogDebug( "Attempting to store new SK {keyId} for created {created}", partition.SystemKeyId, newSystemKeyRecord.Created); if (metastore.Store(partition.SystemKeyId, newSystemKeyRecord.Created, newSystemKeyRecord.ToJson())) { return(systemKey); } else { Logger.LogDebug( "Attempted to store new SK {keyId} but detected duplicate for created {created}, disposing newly created SK", partition.SystemKeyId, systemKey.GetCreated()); DisposeKey(systemKey, null); } } catch (Exception e) { DisposeKey(systemKey, e); throw new AppEncryptionException("Unable to store new System Key", e); } // If we're here, storing of the newly generated key failed above which means we attempted to // save a duplicate key to the metastore. If that's the case, then we know a valid key exists // in the metastore, so let's grab it and return it. newestSystemKeyRecord = LoadLatestKeyRecord(partition.SystemKeyId); if (newestSystemKeyRecord.IsSome) { EnvelopeKeyRecord keyRecord = (EnvelopeKeyRecord)newestSystemKeyRecord; return(keyManagementService.DecryptKey( keyRecord.EncryptedKey, keyRecord.Created, keyRecord.Revoked.IfNone(false))); } else { throw new AppEncryptionException("SystemKey not present after LoadLatestKeyRecord retry"); } }
internal virtual CryptoKey GetLatestOrCreateIntermediateKey() { Option <EnvelopeKeyRecord> newestIntermediateKeyRecord = LoadLatestKeyRecord(partition.IntermediateKeyId); if (newestIntermediateKeyRecord.IsSome) { EnvelopeKeyRecord keyRecord = (EnvelopeKeyRecord)newestIntermediateKeyRecord; // If the key we just got back isn't expired, then just use it if (!IsKeyExpiredOrRevoked(keyRecord)) { try { return(WithExistingSystemKey( keyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException( "Could not find parentKeyMeta (SK) for intermediateKey ")), true, key => DecryptKey(keyRecord, key))); } catch (MetadataMissingException e) { Logger.LogDebug( e, "The SK for the IK ({keyId}, {created}) is missing or in an invalid state. Will create new IK instead.", partition.IntermediateKeyId, keyRecord.Created); } } // If we're here we know we have an expired key. // If we're doing queued rotation, flag it and continue to use the expired key if (cryptoPolicy.IsQueuedKeyRotation()) { // TODO : Queued rotation Logger.LogDebug("Queuing up IK {keyId} for rotation", partition.IntermediateKeyId); try { return(WithExistingSystemKey( keyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException( "Could not find parentKeyMeta (SK) for intermediateKey")), true, key => DecryptKey(keyRecord, key))); } catch (MetadataMissingException e) { Logger.LogDebug( e, "The SK for the IK ({keyId}, {created}) is missing or in an invalid state. Will create new IK instead.", partition.IntermediateKeyId, keyRecord.Created); } } // If we're here then we're doing inline rotation and have an expired key. // Fall through as if we didn't have the key } DateTimeOffset intermediateKeyCreated = cryptoPolicy.TruncateToIntermediateKeyPrecision(DateTimeOffset.UtcNow); CryptoKey intermediateKey = crypto.GenerateKey(intermediateKeyCreated); try { EnvelopeKeyRecord newIntermediateKeyRecord = WithSystemKeyForWrite(systemCryptoKey => new EnvelopeKeyRecord( intermediateKey.GetCreated(), new KeyMeta(partition.SystemKeyId, systemCryptoKey.GetCreated()), crypto.EncryptKey(intermediateKey, systemCryptoKey), false)); Logger.LogDebug( "Attempting to store new IK {keyId}, for created {created}", partition.IntermediateKeyId, newIntermediateKeyRecord.Created); if (metastore.Store( partition.IntermediateKeyId, newIntermediateKeyRecord.Created, newIntermediateKeyRecord.ToJson())) { return(intermediateKey); } else { Logger.LogDebug( "Attempted to store new IK {keyId} but detected duplicate for created {created}, disposing newly created IK", partition.IntermediateKeyId, intermediateKey.GetCreated()); DisposeKey(intermediateKey, null); } } catch (Exception e) { DisposeKey(intermediateKey, e); throw new AppEncryptionException("Unable to store new Intermediate Key", e); } // If we're here, storing of the newly generated key failed above which means we attempted to // save a duplicate key to the metastore. If that's the case, then we know a valid key exists // in the metastore, so let's grab it and return it. // Using a new variable instead of the one above because the WithSystemKeyForWrite use above wants finality Option <EnvelopeKeyRecord> actualLatestIntermediateKeyRecord = LoadLatestKeyRecord(partition.IntermediateKeyId); if (actualLatestIntermediateKeyRecord.IsSome) { EnvelopeKeyRecord keyRecord = (EnvelopeKeyRecord)actualLatestIntermediateKeyRecord; // NOTE: Not wrapping this in try/catch to allow errors to bubble up. If we're missing meta in this flow, something's wrong. return(WithExistingSystemKey( keyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException("Could not find parentKeyMeta (SK) for intermediateKey")), true, key => DecryptKey(keyRecord, key))); } else { throw new AppEncryptionException("IntermediateKey not present after LoadLatestKeyRecord retry"); } }