public FieldLevelEncryptionParams(FieldLevelEncryptionConfig config, string ivValue, string encryptedKeyValue, string oaepPaddingDigestAlgorithmValue = null) { IvValue = ivValue; EncryptedKeyValue = encryptedKeyValue; OaepPaddingDigestAlgorithmValue = oaepPaddingDigestAlgorithmValue; Config = config; }
/// <summary> /// Generate encryption parameters. /// </summary> /// <exception cref="EncryptionException"/> public static FieldLevelEncryptionParams Generate(FieldLevelEncryptionConfig config) { // Generate a random IV var ivBytes = GenerateIv(); var ivValue = EncodingUtils.EncodeBytes(ivBytes, config.ValueEncoding); // Generate an AES secret key var secretKeyBytes = GenerateSecretKey(); // Encrypt the secret key var encryptedSecretKeyBytes = RsaEncryption.WrapSecretKey(config.EncryptionCertificate.GetRSAPublicKey(), secretKeyBytes, config.OaepPaddingDigestAlgorithm); var encryptedKeyValue = EncodingUtils.EncodeBytes(encryptedSecretKeyBytes, config.ValueEncoding); // Compute the OAEP padding digest algorithm var oaepPaddingDigestAlgorithmValue = config.OaepPaddingDigestAlgorithm.Replace("-", string.Empty); return(new FieldLevelEncryptionParams { IvValue = ivValue, EncryptedKeyValue = encryptedKeyValue, OaepPaddingDigestAlgorithmValue = oaepPaddingDigestAlgorithmValue, Config = config, SecretKeyBytes = secretKeyBytes, IvBytes = ivBytes }); }
/// <summary> /// Decrypt parts of a JSON payload using the given parameters and configuration. /// </summary> /// <param name="payload">A JSON string</param> /// <param name="config">A <see cref="FieldLevelEncryptionConfig"/> instance</param> /// <param name="parameters">A <see cref="FieldLevelEncryptionParams"/> instance</param> /// <returns>The updated payload</returns> /// <exception cref="EncryptionException"/> public static string DecryptPayload(string payload, FieldLevelEncryptionConfig config, FieldLevelEncryptionParams parameters = null) { if (payload == null) { throw new ArgumentNullException(nameof(payload)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } try { // Parse the given payload var payloadToken = JToken.Parse(payload); // Perform decryption (if needed) foreach (var jsonPathIn in config.DecryptionPaths.Keys) { var jsonPathOut = config.DecryptionPaths[jsonPathIn]; payloadToken = DecryptPayloadPath(payloadToken, jsonPathIn, jsonPathOut, config, parameters); } // Return the updated payload return(payloadToken.ToString()); } catch (EncryptionException) { throw; } catch (Exception e) { throw new EncryptionException("Payload decryption failed!", e); } }
internal static byte[] WrapSecretKey(FieldLevelEncryptionConfig config, byte[] keyBytes) { try { var publicEncryptionKey = config.EncryptionCertificate.GetRSAPublicKey(); return(publicEncryptionKey.Encrypt(keyBytes, "SHA-256".Equals(config.OaepPaddingDigestAlgorithm) ? RSAEncryptionPadding.OaepSHA256 : RSAEncryptionPadding.OaepSHA512)); } catch (Exception e) { throw new EncryptionException("Failed to wrap secret key!", e); } }
internal static byte[] UnwrapSecretKey(FieldLevelEncryptionConfig config, byte[] keyBytes, string oaepDigestAlgorithm) { try { if (!oaepDigestAlgorithm.Contains("-")) { oaepDigestAlgorithm = oaepDigestAlgorithm.Replace("SHA", "SHA-"); } var decryptionKey = config.DecryptionKey; return(decryptionKey.Decrypt(keyBytes, "SHA-256".Equals(oaepDigestAlgorithm) ? RSAEncryptionPadding.OaepSHA256 : RSAEncryptionPadding.OaepSHA512)); } catch (Exception e) { throw new EncryptionException("Failed to unwrap secret key!", e); } }
private static JToken DecryptPayloadPath(JToken payloadToken, string jsonPathIn, string jsonPathOut, FieldLevelEncryptionConfig config, FieldLevelEncryptionParams parameters) { if (payloadToken == null) { throw new ArgumentNullException(nameof(payloadToken)); } if (jsonPathIn == null) { throw new ArgumentNullException(nameof(jsonPathIn)); } if (jsonPathOut == null) { throw new ArgumentNullException(nameof(jsonPathOut)); } var inJsonToken = payloadToken.SelectToken(jsonPathIn); if (inJsonToken == null) { // Nothing to decrypt return(payloadToken); } // Read and remove encrypted data and encryption fields at the given JSON path JsonUtils.AssertIsObject(inJsonToken, jsonPathIn); var encryptedValueJsonToken = ReadAndDeleteJsonKey(inJsonToken, config.EncryptedValueFieldName); if (IsNullOrEmptyJson(encryptedValueJsonToken)) { // Nothing to decrypt return(payloadToken); } if (!config.UseHttpPayloads() && parameters == null) { throw new InvalidOperationException("Encryption params have to be set when not stored in HTTP payloads!"); } if (parameters == null) { // Read encryption params from the payload var oaepDigestAlgorithmJsonToken = ReadAndDeleteJsonKey(inJsonToken, config.OaepPaddingDigestAlgorithmFieldName); var oaepDigestAlgorithm = IsNullOrEmptyJson(oaepDigestAlgorithmJsonToken) ? config.OaepPaddingDigestAlgorithm : oaepDigestAlgorithmJsonToken; var encryptedKeyJsonToken = ReadAndDeleteJsonKey(inJsonToken, config.EncryptedKeyFieldName); var ivJsonToken = ReadAndDeleteJsonKey(inJsonToken, config.IvFieldName); ReadAndDeleteJsonKey(inJsonToken, config.EncryptionCertificateFingerprintFieldName); ReadAndDeleteJsonKey(inJsonToken, config.EncryptionKeyFingerprintFieldName); parameters = new FieldLevelEncryptionParams(config, ivJsonToken, encryptedKeyJsonToken, oaepDigestAlgorithm); } // Decrypt data var encryptedValueBytes = EncodingUtils.DecodeValue(encryptedValueJsonToken, config.ValueEncoding); var decryptedValueBytes = DecryptBytes(parameters.GetSecretKeyBytes(), parameters.GetIvBytes(), encryptedValueBytes); // Add decrypted data at the given JSON path var decryptedValue = JsonUtils.SanitizeJson(Encoding.UTF8.GetString(decryptedValueBytes)); if ("$".Equals(jsonPathOut)) { // The decrypted JSON is the new body return(JToken.Parse(decryptedValue)); } else { JsonUtils.CheckOrCreateOutObject(payloadToken, jsonPathOut); JsonUtils.AddDecryptedDataToPayload(payloadToken, decryptedValue, jsonPathOut); // Remove the input if now empty inJsonToken = payloadToken.SelectToken(jsonPathIn); if (inJsonToken.Type == JTokenType.Object && !inJsonToken.HasValues) { inJsonToken.Parent.Remove(); } } return(payloadToken); }
private static JToken EncryptPayloadPath(JToken payloadToken, string jsonPathIn, string jsonPathOut, FieldLevelEncryptionConfig config, FieldLevelEncryptionParams parameters) { if (payloadToken == null) { throw new ArgumentNullException(nameof(payloadToken)); } if (jsonPathIn == null) { throw new ArgumentNullException(nameof(jsonPathIn)); } if (jsonPathOut == null) { throw new ArgumentNullException(nameof(jsonPathOut)); } var inJsonToken = payloadToken.SelectToken(jsonPathIn); if (inJsonToken == null) { // Nothing to encrypt return(payloadToken); } if (parameters == null) { // Generate encryption params parameters = FieldLevelEncryptionParams.Generate(config); } // Encrypt data at the given JSON path var inJsonString = JsonUtils.SanitizeJson(inJsonToken.ToString()); var inJsonBytes = Encoding.ASCII.GetBytes(inJsonString); var encryptedValueBytes = EncryptBytes(parameters.GetSecretKeyBytes(), parameters.GetIvBytes(), inJsonBytes); var encryptedValue = EncodingUtils.EncodeBytes(encryptedValueBytes, config.ValueEncoding); // Delete data in clear if (!"$".Equals(jsonPathIn)) { inJsonToken.Parent.Remove(); } else { // We need a JObject (we can't work with a JArray for instance) payloadToken = JObject.Parse("{}"); } // Add encrypted data and encryption fields at the given JSON path JsonUtils.CheckOrCreateOutObject(payloadToken, jsonPathOut); var outJsonToken = payloadToken.SelectToken(jsonPathOut) as JObject; JsonUtils.AddOrReplaceJsonKey(outJsonToken, config.EncryptedValueFieldName, encryptedValue); if (!string.IsNullOrEmpty(config.IvFieldName)) { JsonUtils.AddOrReplaceJsonKey(outJsonToken, config.IvFieldName, parameters.IvValue); } if (!string.IsNullOrEmpty(config.EncryptedKeyFieldName)) { JsonUtils.AddOrReplaceJsonKey(outJsonToken, config.EncryptedKeyFieldName, parameters.EncryptedKeyValue); } if (!string.IsNullOrEmpty(config.EncryptionCertificateFingerprintFieldName)) { JsonUtils.AddOrReplaceJsonKey(outJsonToken, config.EncryptionCertificateFingerprintFieldName, config.EncryptionCertificateFingerprint); } if (!string.IsNullOrEmpty(config.EncryptionKeyFingerprintFieldName)) { JsonUtils.AddOrReplaceJsonKey(outJsonToken, config.EncryptionKeyFingerprintFieldName, config.EncryptionKeyFingerprint); } if (!string.IsNullOrEmpty(config.OaepPaddingDigestAlgorithmFieldName)) { JsonUtils.AddOrReplaceJsonKey(outJsonToken, config.OaepPaddingDigestAlgorithmFieldName, parameters.OaepPaddingDigestAlgorithmValue); } return(payloadToken); }