在EF Core中为数据表按列加密存储

假设有User表

1
2
3
4
5
6
7
public class User : Entity<int>
{
public int Id { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
public string IdentificationNumber { get; set; }
}

其中有身份证号码IdentificationNumber列,需要加密存储,该如何实现?

在这里插入图片描述

创建一个值转换器,继承ValueConverter<TModel, string>类型。其中泛型TModel为实体中属性的类型。

转换器将实体中属性类型,通过AES加密算法,转换为Base64编码字符串类型,存储到数据库中。当从数据库中读取数据时,再通过AES解密算法,将Base64编码字符串类型转换为实体中属性类型。

若实体类型为byte[],则不需要转换为Base64编码字符串类型,直接对二进制数据进行加密和解密。此转换器可以用于加密存储图片、文件等二进制数据。

AES加密算法是一种对称加密算法,加密和解密使用相同的密钥。在加密和解密时,需要指定密钥、初始向量、盐值等参数。在转换器中,将这些参数设置为静态属性,方便在使用时,进行修改。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
public class EncryptionConverter<TModel> : ValueConverter<TModel, string>
{

public const int DefaultKeysize = 256;

public static string DefaultPassPhrase { get; set; }

public static byte[] DefaultInitVectorBytes { get; set; }

public static byte[] DefaultSalt { get; set; }
public EncryptionConverter()
: base(
x => Encrypt(x),
x => Decrypt(x))
{
DefaultPassPhrase = "gsKnGZ041HLL4IM8";
DefaultInitVectorBytes = Encoding.ASCII.GetBytes("jkE49230Tf093b42");
DefaultSalt = Encoding.ASCII.GetBytes("hgt!16kl");
}

private static string Encrypt(TModel input)
{
try
{
byte[] inputData = input switch
{
string => Encoding.UTF8.GetBytes(input.ToString()),
byte[] => input as byte[],
_ => null,
};

using (var password = new Rfc2898DeriveBytes(DefaultPassPhrase, DefaultSalt))
{
var keyBytes = password.GetBytes(DefaultKeysize / 8);
using (var symmetricKey = Aes.Create())
{
symmetricKey.Mode = CipherMode.CBC;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, DefaultInitVectorBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(inputData, 0, inputData.Length);
cryptoStream.FlushFinalBlock();
var cipherTextBytes = memoryStream.ToArray();
var rawString = Convert.ToBase64String(cipherTextBytes);
return rawString;

}
}
}
}
}
}
catch (Exception ex)
{
LogHelper.LogException(ex);
return input.ToString();
}

}

private static TModel Decrypt(string input)
{
try
{
var cipherTextBytes = Convert.FromBase64String(input);

using (var password = new Rfc2898DeriveBytes(DefaultPassPhrase, DefaultSalt))
{
var keyBytes = password.GetBytes(DefaultKeysize / 8);
using (var symmetricKey = Aes.Create())
{
symmetricKey.Mode = CipherMode.CBC;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, DefaultInitVectorBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
int totalDecryptedByteCount = 0;
while (totalDecryptedByteCount < plainTextBytes.Length)
{
var decryptedByteCount = cryptoStream.Read(
plainTextBytes,
totalDecryptedByteCount,
plainTextBytes.Length - totalDecryptedByteCount
);

if (decryptedByteCount == 0)
{
break;
}

totalDecryptedByteCount += decryptedByteCount;
}
byte[] outputData = null;
if (typeof(TModel) == typeof(string))
{
var outputData = Encoding.UTF8.GetString(plainTextBytes, 0, totalDecryptedByteCount);
return (TModel)Convert.ChangeType(outputData, typeof(TModel));

}
else if (typeof(TModel) == typeof(byte[]))
{
var outputData = plainTextBytes as byte[];
return (TModel)Convert.ChangeType(outputData, typeof(TModel));

};
return default;

}
}
}
}
}

}
catch (Exception ex)
{
// 记录异常
// LogHelper.LogException(ex);
return (TModel)Convert.ChangeType(input, typeof(TModel));
}

}
}

DbContext中,重写OnModelCreating方法,为User表的IdentificationNumber列,添加值转换器。

1
2
3
4
5
6
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().Property(c => c.IdentificationNumber).HasConversion<EncryptionConverter<string>>();
}

再次调用Add方法插入数据时,可以看到IdentificationNumber列已被加密了

在这里插入图片描述

在EF Core中为数据表按列加密存储

https://blog.matoapp.net/posts/7926cf99/

作者

林晓lx

发布于

2023-04-05

更新于

2024-09-11

许可协议

评论