使用 Json.NET,在序列化对象时如何加密任何类型的选定属性?

2024-06-20

我想概括一下这个答案 https://stackoverflow.com/a/29240043/3744182 by 布赖恩·罗杰斯 https://stackoverflow.com/users/10263/brian-rogers to 序列化对象时如何加密选定的属性? https://stackoverflow.com/q/29196809/3744182使 Json.NET 自动加密任何类型的属性[JsonEncrypt]已应用——不仅仅是该答案支持的字符串值属性。如何实施?

目前我正在尝试通过调用自定义序列化程序和提供程序来实现基于属性的加密/解密,如下所示:

[Test]
public void MessageJsonSerializer_TopLevelModel_Success()
{    
    // arrange    
    var key = _fixture.Create<Guid>().ToString().ToUpper();    
    var model = _fixture.Create<TopLevelModel>();    
    var serializer = new MessageJsonSerializer<TopLevelModel>(_encryptionService);    

    // act    
    var serializedEncrypted = serializer.SerializeEncrypted(model, key);    
    var deserializedDecrypted = serializer.DeserializeEncrypted(serializedEncrypted, key);    

    // assert    
    model.Should().BeEquivalentTo(deserializedDecrypted, "After de/serialization models should be equal");    
    serializer.Invoking(x => x.Deserialize(serializedEncrypted))        
        .Should()        
        .Throw<SerializationException>("Full sub-class will be converted to encrypted string - couldn't be deserialized");}

模型在哪里:

public class TopLevelModel
{    
    [JsonEncrypt]    
    public string PrivateText { get; set; }    
    public string Note { get; set; }    

    [JsonEncrypt]
    public IEnumerable<InternalFlatStringModel> FlatStringModels { get; set; }    

    public class InternalFlatStringModel : IEncryptedDTO    
    {        
        [JsonEncrypt]        
        public string PrivateText { get; set; }        
        public string Note { get; set; }    
    }
}

提供者:

public class EncryptedObjectValueProvider : IValueProvider
{
    private readonly IEncryptionService _encryptionService;
    private readonly PropertyInfo _targetProperty;
    private readonly string _encryptionKey;

    public EncryptedObjectValueProvider(IEncryptionService encryptionService, PropertyInfo targetProperty, string encryptionKey)
    {
        _encryptionService = encryptionService;
        _targetProperty = targetProperty;
        _encryptionKey = encryptionKey;

    }

    public object GetValue(object target)
    {
        var jsonString = JsonConvert.SerializeObject(_targetProperty.GetValue(target), Formatting.None);
        return _encryptionService.EncryptStringAES(jsonString, _encryptionKey);
    }
    public void SetValue(object target, object value)
    {
        // --> never hits this one
        var decryptedValue = _encryptionService.DecryptStringAES((string) value, _encryptionKey);
        var decryptedObject = JsonConvert.DeserializeObject(decryptedValue);
        _targetProperty.SetValue(target, decryptedObject);
    }
}

问题是 SetValue 永远不会被嵌套对象 InternalFlatStringModel 调用。获取 System.Runtime.Serialization.SerializationException:转换值%加密对象%时出错。


布莱恩·罗杰的自定义合约解析器 https://stackoverflow.com/a/29240043/3744182使用自定义IValueProvider https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_IValueProvider.htm注入逻辑来加密和解密字符串值属性。您想将其概括为加密和解密任何类型的财产通过对 JSON 进行嵌套序列化或反序列化,然后对生成的 JSON 字符串进行加密或解密。不幸的是,自定义值提供者并不真正适合此目的。值提供者的职责是从数据库中设置和获取值。它的设计目的不是对该值进行任意复杂的转换,也不是进行嵌套序列化。 (当前JsonSerializer甚至没有传递给价值提供者。)

代替价值提供者的是custom JsonConverter https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm可以由联系人解析器应用来执行必要的嵌套序列化和加密。 AJsonConverter可以访问当前的序列化器,其职责包括将 JSON 转换为最终数据模型,因此它更适合此任务。

首先,定义以下属性和接口:

public interface IEncryptionService
{
    /// <summary>
    /// Encrypt the incoming string into another Utf8 string using whatever algorithm, password and salt are appropiate.
    /// </summary>
    /// <param name="input">The string to be encrypted</param>
    /// <returns>The encypted string</returns>
    public string Encrypt(string input);
    /// <summary>
    /// Decrypt the incoming string into another Utf8 string using whatever algorithm, password and salt are appropiate.
    /// </summary>
    /// <param name="input">The encrypted string</param>
    /// <returns>The decrypted string value</returns>
    public string Decrypt(string input);
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class JsonEncryptAttribute : Attribute
{
}

这里我假设需要_encryptionKey盐将被封装在您的具体实现中IEncryptionService.

接下来,定义以下合约解析器:

public class EncryptedPropertyContractResolver : DefaultContractResolver
{
    IEncryptionService EncryptionService { get; }

    public EncryptedPropertyContractResolver(IEncryptionService encryptionService) => this.EncryptionService = encryptionService ?? throw new ArgumentNullException(nameof(encryptionService));

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        return ApplyEncryption(property);
    }
    
    protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo)
    {
        var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo);
        return ApplyEncryption(property, matchingMemberProperty);
    }
    
    JsonProperty ApplyEncryption(JsonProperty property, JsonProperty matchingMemberProperty = null)
    {
        if ((matchingMemberProperty ?? property).AttributeProvider.GetAttributes(typeof(JsonEncryptAttribute), true).Any())
        {
            if (property.ItemConverter != null)
                throw new NotImplementedException("property.ItemConverter");
            property.Converter = new EncryptingJsonConverter(EncryptionService, property.Converter);
        }
        return property;
    }
    
    class EncryptingJsonConverter : JsonConverter
    {
        IEncryptionService EncryptionService { get; }
        JsonConverter InnerConverter { get; }

        public EncryptingJsonConverter(IEncryptionService encryptionService, JsonConverter innerConverter)
        {
            this.EncryptionService = encryptionService ?? throw new ArgumentNullException(nameof(encryptionService));
            this.InnerConverter = innerConverter;
        }

        public override bool CanConvert(Type objectType) => throw new NotImplementedException(nameof(CanConvert));          

        object ReadInnerJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (InnerConverter?.CanRead == true)
                return InnerConverter.ReadJson(reader, objectType, existingValue, serializer);
            else
                return serializer.Deserialize(reader, objectType);
        }
        
        public void WriteInnerJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (InnerConverter?.CanWrite == true)
                InnerConverter.WriteJson(writer, value, serializer);
            else
                serializer.Serialize(writer, value);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
                return null;
            else if (reader.TokenType != JsonToken.String)
                throw new JsonSerializationException(string.Format("Unexpected token type {0}", reader.TokenType));
            var encryptedString = (string)reader.Value;
            var jsonString = EncryptionService.Decrypt(encryptedString);
            using (var subReader = new JsonTextReader(new StringReader(jsonString)))
                return ReadInnerJson(subReader.MoveToContentAndAssert(), objectType, existingValue, serializer);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            using var textWriter = new StringWriter();
            using (var subWriter = new JsonTextWriter(textWriter))
                WriteInnerJson(subWriter, value, serializer);
            var encryptedString = EncryptionService.Encrypt(textWriter.ToString());
            writer.WriteValue(encryptedString);
        }
    }
}

public static partial class JsonExtensions
{
    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

现在您可以申请[JsonEncrypt]如问题中所示到您的模型,并按如下方式序列化和反序列化它的实例:

var resolver = new EncryptedPropertyContractResolver(_encryptionService);
var settings = new JsonSerializerSettings
{
    ContractResolver = resolver,
};          
var encryptedJson = JsonConvert.SerializeObject(model, Formatting.Indented, settings);
var model2 = JsonConvert.DeserializeObject<TopLevelModel>(encryptedJson, settings);

Notes:

  • 适合哪些收藏JsonPropertyAttribute.ItemConverterType https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonPropertyAttribute_ItemConverterType.htm已设置未实施。

  • 我尚未测试参数化构造函数参数的加密。

  • 这个答案没有解决如何IEncryptionService应实施。加密算法的安全实施超出了其范围。

演示小提琴here https://dotnetfiddle.net/sQZd0M.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Json.NET,在序列化对象时如何加密任何类型的选定属性? 的相关文章

随机推荐

  • 将流转换为 IntStream

    我有一种感觉 我在这里错过了一些东西 我发现自己做了以下事情 private static int getHighestValue Map
  • static 变量和 const 变量有什么区别?

    有人可以解释一下 a 之间的区别吗static and const多变的 恒定值不能改变 静态变量存在于函数或类中 而不是实例或对象中 这两个概念并不相互排斥 可以一起使用
  • 使用主题交换运行多个 Celery 任务

    我正在用 Celery 替换一些自制代码 但很难复制当前的行为 我期望的行为如下 创建新用户时 应向tasks与交换user created路由键 该消息应该触发两个 Celery 任务 即send user activate email
  • 将 c3p0 与 Tomcat 8 数据源结合使用

    我有一个加载了数据源的 tomcat 8 服务器 我想知道是否可以将这个DataSource与c3p0连接池管理结合使用 到目前为止 这是我尝试过的 上下文 xml
  • 检测并缩短字符串中的所有网址

    假设我有一条字符串消息 您应该将 file zip 上传到http google com extremelylonglink zip http google com extremelylonglink zip not https stack
  • 使用 Angular 下载具有动态 src 的脚本

    Angular 提供了通过动态名称动态加载模板的方法ng include 该部分中的内联 JS 和 CSS 可以正常加载 但没有一个好的方法来下载带有动态 url 的脚本 我们需要下载脚本 相对于调用它们的 html 部分的路径 即我们有一
  • C 编程:seg 错误、printf 和相关的怪癖[重复]

    这个问题在这里已经有答案了 正如许多年轻的程序员所做的那样 我了解到在代码中的不同点插入大量 here1 here2 等打印到控制台语句的有用性 以找出我的程序何时出错 在我的计算机科学学习过程中 这种强力调试技术已经拯救了我很多很多次 然
  • 在 R 中使用 Huggingface Transformer 模型

    我正在尝试在 R 中使用不同的 Huggingface 模型 这是通过 reticulate 导入 Transformer 包来实现的 谢谢 https rpubs com eR ic transfoRmers https rpubs co
  • NoSuchMethodError:尝试调用非函数,例如 null:'dart.global.firebase.auth'

    Flutter 新手 我怀疑在尝试设置 Firebase Auth 时错过了一些非常简单的事情 一直在网上寻找解决方案 大多数人要求您仔细检查 firebase auth js 是否正确包含在 index html 文件中 这样就完成了 下
  • 在 Wordpress 站点中进行 AJAX 调用时出现问题

    我在使用 Wordpress 站点功能的 AJAX 部分时遇到了一些问题 该功能接受在表单上输入的邮政编码 使用 PHP 函数来查找邮政编码是否引用特定位置并返回到该位置的永久链接 我的第一个问题是关于我构建的表单 现在我的表单操作是空白的
  • .NET 内存不足故障排除

    在阅读了几篇有关 NET 技术中的内存的启发性文章后 Out of Memory 不是指物理内存 https learn microsoft com en us archive blogs ericlippert out of memory
  • Android 深度链接至 Instagram 应用

    Instagram 已经发布了 iOS 深层链接的 url 方案 但尚未为 Android 创建文档 有没有办法深入链接到 Android 上的 Instagram 应用程序 以转到 Instagram 应用程序中的特定位置 例如 Inst
  • 循环预定义值

    有没有办法在 oracle 中执行 for every 如下所示 begin for VAR in 1 2 5 loop dbms output put line The value VAR end loop end 我知道你可以这样做 b
  • DownloadStringAsync 首次调用时会阻止线程 14 秒

    这种情况只发生在我的一台机器上 我认为是环境配置问题 所有计算机均运行 ESET Smart Security 软件防火墙 有任何想法吗 using System using System Net using System Diagnost
  • Excel VSTO 工作簿新活动

    在 Excel 2007 的一个 Excel 插件项目中 我需要检查创建新工作簿的事件 我还需要捕捉 Workbook Open 事件 我很容易做到这一点 在我在互联网上的研究中 我发现了以下内容 打开任何工作簿时都会引发 Applicat
  • 如何解决asp.net core mvc项目中的“未找到视图”异常

    我正在尝试使用 VS Code 创建一个在 OSX 上运行的 ASP NET Core MVC 测试应用程序 访问默认主页 索引 或我尝试过的任何其他视图 时 我收到 未找到视图 异常 这是启动配置 public void Configur
  • 从Django中具有外键关系的两个表中检索数据? [复制]

    这个问题在这里已经有答案了 This is my models py file from django db import models class Author models Model first name models CharFie
  • 使用 Kentor.AuthServices.StubIdp 作为生产 IDP

    我正在尝试在我的应用程序中实现 IDP SAML2 服务器 鉴于我的应用程序拥有所需的所有数据 我不希望我的任何合作伙伴要求我们的客户在他们这边注册 我对 SAML2 协议不是很熟悉 我找到了这个项目Kentor AuthServices
  • 透视变换矩阵的计算

    给定 3D 空间中的一个点 如何计算齐次坐标中的矩阵 将该点投影到平面上z d 其中原点是投影中心 好吧 让我们尝试解决这个问题 扩展伊曼纽尔的答案 Assuming如果您的视图向量直接沿着 Z 轴 则所有尺寸都必须按视图平面距离的比例进行
  • 使用 Json.NET,在序列化对象时如何加密任何类型的选定属性?

    我想概括一下这个答案 https stackoverflow com a 29240043 3744182 by 布赖恩 罗杰斯 https stackoverflow com users 10263 brian rogers to 序列化