/// <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))); }
/// <summary> /// Initializes a new instance of the <see cref="EnvelopeKeyRecord"/> class using the provided parameters. An /// envelope key record is an internal data structure used to represent a system key, intermediate key or a data /// row key. It consists of an encrypted key and metadata referencing the parent key in the key hierarchy used /// to encrypt it (i.e. its Key Encryption Key). /// </summary> /// /// <param name="created">Creation time of the <paramref name="encryptedKey"/>.</param>. /// <param name="parentKeyMeta">The <see cref="KeyMeta"/> for encryption keys.</param> /// <param name="encryptedKey">The encrypted key (a system key, intermediate key or a data row key.</param> /// <param name="revoked">The revocation status of the encrypted key.</param> public EnvelopeKeyRecord(DateTimeOffset created, KeyMeta parentKeyMeta, byte[] encryptedKey, bool?revoked) { Created = created; ParentKeyMeta = parentKeyMeta; EncryptedKey = encryptedKey; Revoked = revoked ?? Option <bool> .None; }
internal virtual T WithSystemKeyForWrite <T>(Func <CryptoKey, T> functionWithDecryptedSystemKey) { // Try to get latest from cache. If not found or expired, get latest or create CryptoKey systemKey = systemKeyCache.GetLast(); if (systemKey == null || IsKeyExpiredOrRevoked(systemKey)) { systemKey = GetLatestOrCreateSystemKey(); // Put the key into our cache if allowed if (cryptoPolicy.CanCacheSystemKeys()) { try { KeyMeta systemKeyMeta = new KeyMeta(partition.SystemKeyId, systemKey.GetCreated()); Logger.LogDebug("Attempting to update cache for SK {keyMeta}", systemKeyMeta); systemKey = systemKeyCache.PutAndGetUsable(systemKeyMeta.Created, systemKey); } catch (Exception e) { DisposeKey(systemKey, e); throw new AppEncryptionException("Unable to update cache for SystemKey", e); } } } return(ApplyFunctionAndDisposeKey(systemKey, functionWithDecryptedSystemKey)); }
/// <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="intermediateKeyMeta">The <see cref="KeyMeta"/> 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(KeyMeta intermediateKeyMeta) { EnvelopeKeyRecord intermediateKeyRecord = LoadKeyRecord(intermediateKeyMeta.KeyId, intermediateKeyMeta.Created); return(WithExistingSystemKey( intermediateKeyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException("Could not find parentKeyMeta (SK) for intermediateKey")), false, key => DecryptKey(intermediateKeyRecord, key))); }
/// <inheritdoc/> public override bool Equals(object obj) { if (this == obj) { return(true); } if (obj == null || GetType() != obj.GetType()) { return(false); } KeyMeta other = (KeyMeta)obj; return(Equals(KeyId, other.KeyId) && Equals(Created, other.Created)); }
/// <summary> /// Calls a function using a decrypted system key that was previously used. /// </summary> /// <typeparam name="T">The type that the <paramref name="functionWithSystemKey"/> returns.</typeparam> /// /// <returns>The result returned by the <paramref name="functionWithSystemKey"/>.</returns> /// /// <param name="systemKeyMeta">system key meta used previously to write an IK.</param> /// <param name="treatExpiredAsMissing">if <value>true</value>, will throw a /// <see cref="MetadataMissingException"/> if the key is expired/revoked.</param> /// <param name="functionWithSystemKey">the function to call using the decrypted system key.</param> /// /// <exception cref="MetadataMissingException">If the system key is not found, or if its expired/revoked and /// <see cref="treatExpiredAsMissing"/> is <value>true</value>.</exception> internal virtual T WithExistingSystemKey <T>( KeyMeta systemKeyMeta, bool treatExpiredAsMissing, Func <CryptoKey, T> functionWithSystemKey) { // Get from cache or lookup previously used key CryptoKey systemKey = systemKeyCache.Get(systemKeyMeta.Created); if (systemKey == null) { systemKey = GetSystemKey(systemKeyMeta); // Put the key into our cache if allowed if (cryptoPolicy.CanCacheSystemKeys()) { try { Logger.LogDebug("Attempting to update cache for SK {keyMeta}", systemKeyMeta); systemKey = systemKeyCache.PutAndGetUsable(systemKeyMeta.Created, systemKey); } catch (Exception e) { DisposeKey(systemKey, e); throw new AppEncryptionException("Unable to update cache for SystemKey", e); } } } if (IsKeyExpiredOrRevoked(systemKey)) { if (treatExpiredAsMissing) { DisposeKey(systemKey, null); throw new MetadataMissingException("System key is expired/revoked, keyMeta = " + systemKeyMeta); } // There's an implied else here if (cryptoPolicy.NotifyExpiredSystemKeyOnRead()) { // TODO: Send notification that an SK is expired Logger.LogDebug("NOTIFICATION: Expired SK {keyMeta} in use during read", systemKeyMeta); } } return(ApplyFunctionAndDisposeKey(systemKey, functionWithSystemKey)); }
/// <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()); } }
/// <summary> /// Calls a function using a decrypted intermediate key that was previously used. /// </summary> /// <typeparam name="T">The type that the <paramref name="functionWithIntermediateKey"/> returns.</typeparam> /// /// <returns>The result returned by the <paramref name="functionWithIntermediateKey"/>.</returns> /// /// <param name="intermediateKeyMeta">intermediate key meta used previously to write a DRR.</param> /// <param name="functionWithIntermediateKey">the function to call using the decrypted intermediate key.</param> /// /// <exception cref="MetadataMissingException">If the intermediate key is not found, or it has missing system /// key info.</exception> internal virtual T WithIntermediateKeyForRead <T>( KeyMeta intermediateKeyMeta, Func <CryptoKey, T> functionWithIntermediateKey) { // Get from cache or lookup previously used key CryptoKey intermediateKey = intermediateKeyCache.Get(intermediateKeyMeta.Created); if (intermediateKey == null) { intermediateKey = GetIntermediateKey(intermediateKeyMeta.Created); // Put the key into our cache if allowed if (cryptoPolicy.CanCacheIntermediateKeys()) { try { Logger.LogDebug( "Attempting to update cache for IK {keyId} with created {created}", partition.IntermediateKeyId, intermediateKey.GetCreated()); intermediateKey = intermediateKeyCache.PutAndGetUsable(intermediateKey.GetCreated(), intermediateKey); } catch (Exception e) { DisposeKey(intermediateKey, e); throw new AppEncryptionException("Unable to update cache for Intermediate key", e); } } } if (cryptoPolicy.NotifyExpiredIntermediateKeyOnRead() && IsKeyExpiredOrRevoked(intermediateKey)) { // TODO : Send notification that a DRK is using an expired IK Logger.LogDebug("NOTIFICATION: Expired IK {keyMeta} in use during read", intermediateKeyMeta); } return(ApplyFunctionAndDisposeKey(intermediateKey, functionWithIntermediateKey)); }
/// <summary> /// Initializes a new instance of the <see cref="EnvelopeKeyRecord"/> class using the provided parameters. An /// envelope key record is an internal data structure used to represent a system key, intermediate key or a data /// row key. It consists of an encrypted key and metadata referencing the parent key in the key hierarchy used /// to encrypt it (i.e. its Key Encryption Key). /// </summary> /// /// <param name="created">Creation time of the <paramref name="encryptedKey"/>.</param>. /// <param name="parentKeyMeta">The <see cref="KeyMeta"/> for encryption keys.</param> /// <param name="encryptedKey">The encrypted key (a system key, intermediate key or a data row key.</param> public EnvelopeKeyRecord(DateTimeOffset created, KeyMeta parentKeyMeta, byte[] encryptedKey) : this(created, parentKeyMeta, encryptedKey, null) { }