/// <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 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"); } }