【问题标题】:MongoDB Serialization C# - Adding Additional Encrypted Field PropertiesMongoDB 序列化 C# - 添加其他加密字段属性
【发布时间】:2018-07-08 19:32:57
【问题描述】:

我正在尝试在 c# 中编写一个 MongoDb 序列化程序,它允许我通过 [Encrypt()] 属性装饰属性,然后在运行时它允许我生成一个名为 PropertyName_Encrypted 将包含加密值。

在反序列化时,将在父属性中设置加密属性值,以便该属性的默认 GET 始终返回加密值。然后,用户将在对象上调用可选的 Decrypt() 方法来获取解密后的值。

在此过程中,我遇到了一些有趣的挑战:

  1. 在序列化当前元素时,如何向文档添加其他属性?如何获取当前元素的名称?

  2. 有没有办法从文档/对象中读取特定属性?例如假设我想传递一个对称加密密钥并读取它以在序列化当前元素时加密数据?有什么办法可以做到吗?

以下是我到目前为止所做的事情:

  1. 我已经建立了一个加密属性如下:

    [AttributeUsage(AttributeTargets.Property)]
     public class EncryptAttribute : Attribute
    {
    private readonly EncryptedFieldType _fieldType;
    private readonly bool _tokenizeDisplay;
    private readonly string _encryptedFieldName;
    
    /// <summary>
    /// 
    /// </summary>
    /// <param name="fieldType">The field type to encrypt. Useful if display needs to show some formatting. If no formatting is necessary, simply set to "Other".</param>
    /// <param name="tokenizeDisplay">If set to true, will persist the tokenized value in the original field for display purposes.</param>
    /// <param name="encryptedFieldName">Optional. If set, will save the encrypted value in the field name specified. By default all encrypted field values are stored in the corresponding _Encrypted field name. So EmailAddress field if encrypted, would have value under EmailAddress_Encrypted.</param>
    public EncryptAttribute(EncryptedFieldType fieldType, bool tokenizeDisplay, string encryptedFieldName = "")
    {
        _fieldType = fieldType;
        _tokenizeDisplay = tokenizeDisplay;
        _encryptedFieldName = encryptedFieldName;
    }
    }
    
  2. 我在启动时读取了此属性,并将加密序列化程序添加到使用此属性修饰的属性中。这样做的代码是这样的:

    var assemblies = AppDomain.CurrentDomain.GetAssemblies()
            .Where(x => x.FullName.StartsWith("MongoCustomSerializer"))
            .ToList();
        var mapper = new Mapper();
        foreach (var assembly in assemblies)
        {
            mapper.Map(assembly);
        }
    
  3. 映射器只需检查文档中哪些属性具有 Encrypt 属性即可添加序列化程序:

    public sealed class Mapper
    {
        public void Map(Assembly assembly)
        {
        var encryptableTypes = assembly.GetTypes().Where(p =>
            typeof(IEncryptable).IsAssignableFrom(p) && p.IsClass && !p.IsInterface && !p.IsValueType &&
            !p.IsAbstract).ToList();
    
        if (encryptableTypes.Any())
        {
            foreach (var encryptableType in encryptableTypes)
            {
                Map(encryptableType);
            }
        }
    
    }
    
    private void Map(Type documentType)
    {
        var properties =
            documentType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        if (properties.Length <= 0)
        {
            return;
        }
    
        foreach (var property in properties)
        {
            RegisterEncrpytionSerializer(property, typeof(EncryptAttribute), documentType);
        }
    }
    
    private void RegisterEncrpytionSerializer(PropertyInfo property, Type encryptAttributeType, Type documentType)
    {
        var encryptAttributes = property.GetCustomAttributes(encryptAttributeType, false).ToList();
        if (!encryptAttributes.Any()) return;
    
        var memberMap = BsonClassMap.LookupClassMap(documentType).GetMemberMap(property.Name);
        memberMap?.SetSerializer(new EncryptionSerializer());
    }
    

    }

在我的单元测试中,我收到一条错误消息,指出 Bson 类映射已被冻结。即使我想办法绕过它,这个 EncryptionSerializer 类将如何工作到我可以编写附加属性的地方?

很想看看是否有人可以提供帮助!干杯!

更新 1 - 我能够解决 FREEZE 错误。 LookupClassMap 似乎冻结了成员和类映射信息。

This change from the link 允许我处理这个问题:

private void RegisterEncrpytionSerializer(PropertyInfo property, Type encryptAttributeType, Type documentType)
    {
        var encryptAttributes = property.GetCustomAttributes(encryptAttributeType, false).ToList();
        if (!encryptAttributes.Any()) return;

        var classMapDefinition = typeof(BsonClassMap<>);
        var classMapType = classMapDefinition.MakeGenericType(documentType);
        var classMap = (BsonClassMap)Activator.CreateInstance(classMapType);
        classMap.AutoMap();
        var memberMap = classMap.GetMemberMap(property.Name);
        memberMap?.SetSerializer(new KeyVaultEncryptionSerializer(memberMap.ElementName));
    }

【问题讨论】:

  • 您能否分享一下您的 EncryptionSerializer 目前的样子?
  • 您好,我的加密序列化器只是实现了 IBsonSerializer。我永远无法完成它,因为我需要访问另一个字段来获取密钥信息。让我尽可能地重新写一遍,看看我是否可以发布一个 Gist。
  • 你好@marcofo88,这里是要点:gist.github.com/amarwadi/72e40954095f902d55b4ab7b9f61f183 你可以看到我的计划是提取 BSON 文档中已经可用的另一个属性,然后用它来加密当前值。问题是,无法从 BSON 文档中获取任何其他属性。
  • 仅供参考,有 Ruby Gems 已经这样做了。我想构建一个任何人都可以使用的可插拔组件,只要他们设置了主密钥和内容加密密钥。 compose.com/articles/…

标签: c# mongodb mongodb-.net-driver


【解决方案1】:

您是否使用服务来保存/检索实际调用数据库的项目?

我认为您应该将写入/读取加密值的责任转移到调用服务(即存储库实现)而不是 BsonSerializer。

对我来说,加密/解密是持久层的一部分,并且在需要时不会在应用程序中处理,这对我来说是有意义的。

您的实现仅针对您要序列化的指定属性。它创建另一个属性是没有意义的。

第二个想法是,您建议的使用基于Decrypt() 更改值的属性的方法可能不是一个好主意,因为它会使您的代码不可预测且难以阅读。让你的属性变得非常简单。

如果您可以通过调用一个方法来解密属性,它真正为您的代码提供了哪些额外的安全性?

如果您仍然需要Decrypt(),建议您创建返回解密值的解密方法,例如GetUnencryptedCode() 等,它也可以是扩展方法,但仍然不是可读属性。

您还应该考虑使用 SecureString,具体取决于您的用例。

【讨论】:

  • 你好乔纳斯,我明白这一点。我最终最终手动执行此操作。我的目标是自动执行此操作,以便某人可以简单地向任何对象添加 IEncryptable 接口,然后提供主密钥 Uri 以及内容加密密钥(可通过 IEncryptable 接口获得),然后简化保存过程而无需额外的仪式。是的,我也了解解密过程。我的计划是默认情况下不解密,即默认情况下将加密值放在属性中,并需要额外的请求来解密。自动加密是关键。
  • 请检查我上面的评论,有 Ruby Gems 已经这样做了,我试图在 C# 中复制它。 compose.com/articles/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 2021-05-21
相关资源
最近更新 更多