对于小量数据的加密,我们可以使用DPAPI,对称密钥,非对称密钥等。
对于大量数据的加密,非对称密钥加密不仅麻烦,而且速度也很慢,同时还要对公钥和密钥进行保密。
使用对称密钥,速度是相当快的,但仍然要处理密钥的问题,当然这种密钥也有时效性,因为它容易被破解。所出一般情况下用于网络上的会话传输,SSL中用的较多。
对于不想保存密钥,而又要加密和解密来说,DPAPI可能显得简单的多,而且也不需要自己写太多的代码,安全性也得到一定的保证,同时不需要保存密钥,一举多得。
使用举例,下面就DPAPI加密和解密数据库连接字符串举例:
1
string dbconn = "Provider=SQLOLEDB;SERVER={0};uid={1};pwd={2};database={3}";
2
DataProtector dataProtector = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
3
byte[] bts = Encoding.Default.GetBytes(dbconn);
4
byte[] bytes = dataProtector.Encrypt(bts, null);//加密
5
string values = Encoding.Default.GetString(dataProtector.Decrypt(bytes, null));//解密
6

2

3

4

5

6

下面是对DPAPI的封装。
1
using System;
2
using System.Runtime.InteropServices;
3
4
namespace CNetware.Data.DPAPI
5

{
6
/**//// <summary>
7
/// DataProtector 是使用 DPAPI 来加密和解密数据的托管库。
8
/// Web 应用程序经常需要在应用程序配置文件中存储与安全性密切相关的数据,
9
/// 如数据库连接字符串和服务帐户凭据。出于安全性考虑,决不要以明文形式存
10
/// 储此类信息,而一定要在存储之前进行加密。本类是一个托管类库,它用于封装
11
/// 对数据保护 API (DPAPI) 的调用以使用基于计算机或用户的密钥存储来加密和
12
/// 解密数据。可随后从其他托管应用程序使用该库,如 ASP.NET Web 应用程序、Web
13
/// 服务以及企业服务应用程序。
14
/// DPAPI 是加密 API (Crypto API) 的一部分并且是在 crypt32.dll 中实现的。它
15
/// 包含两个方法:CryptProtectData 和 CryptUnprotectData,本类库中方法被引用
16
/// 成了私有方法,在类外不可访问。
17
/// DPAPI 特别有用,因为它能够消除使用密码的应用程序所带来的密钥管理问题。虽
18
/// 然加密能确保数据安全,但您必须采取额外的步骤来确保密钥的安全。DPAPI 使用
19
/// 与 DPAPI 函数的调用代码关联的用户帐户的密码,以便派生加密密钥。因此,是
20
/// 操作系统(而非应用程序)管理着密钥。
21
///
22
/// DPAPI 能够与计算机存储或用户存储(需要一个已加载的用户配置文件)配合使用。
23
/// DPAPI 默认情况下用于用户存储,但您可以通过将 CRYPTPROTECT_LOCAL_MACHINE
24
/// 标志传递给 DPAPI 函数来指定使用计算机存储。
25
///
26
/// 这种用户配置文件方式提供了一个额外的安全层,因为它限制了哪些用户能访问机
27
/// 密内容。只有加密该数据的用户才能解密该数据。但是,当通过 ASP.NET Web 应用
28
/// 程序使用 DPAPI 时,使用用户配置文件需要您执行额外的开发工作,因为您需要采
29
/// 取明确的步骤来加载和卸载用户配置文件(ASP.NET 不会自动加载用户配置文件)。
30
///
31
/// 计算机存储方式更容易开发,因为它不需要管理用户配置文件。但是,除非使用一个
32
/// 附加的熵参数,否则并不安全,因为该计算机的任何用户都可以解密数据。(熵是一
33
/// 个设计用来使解密机密内容更为困难的随机值)。使用附加的熵参数出现的问题在于
34
/// 它必须由应用程序安全地存储起来,这带来了另一个密钥管理问题。
35
///
36
/// 注意:如果您将 DPAPI 和计算机存储一起使用,那么加密字符串仅适用于给定的计
37
/// 算机,因此您必须在每台计算机上生成加密数据。不要在场或群集中将加密数据从一
38
/// 台计算机复制到另一台计算机。如果将 DPAPI 和用户存储一起使用,则可以用一个漫
39
/// 游的用户配置文件在任何一台计算机上解密数据。
40
/// </summary>
41
public class DataProtector
42
{
43
/**//// <summary>
44
/// 用于存储二进制数据流,该结构体有两个成员
45
/// </summary>
46
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
47
internal struct DATA_BLOB
48
{
49
/**//// <summary>
50
/// 用于记录数据长度
51
/// </summary>
52
public int cbData;
53
/**//// <summary>
54
/// 用于存储二进制数据
55
/// </summary>
56
public IntPtr pbData;
57
}
58
/**//// <summary>
59
///
60
/// </summary>
61
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
62
internal struct CRYPTPROTECT_PROMPTSTRUCT
63
{
64
public int cbSize;
65
public int dwPromptFlags;
66
public IntPtr hwndApp;
67
public String szPrompt;
68
}
69
private static IntPtr NullPtr = (IntPtr)0;
70
71
private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1;
72
73
private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;
74
75
/**//// <summary>
76
///
77
/// </summary>
78
/// <param name="pDataIn"></param>
79
/// <param name="szDataDescr"></param>
80
/// <param name="pOptionalEntropy"></param>
81
/// <param name="pvReserved"></param>
82
/// <param name="pPromptStruct"></param>
83
/// <param name="dwFlags"></param>
84
/// <param name="pDataOut"></param>
85
/// <returns></returns>
86
[DllImport("Crypt32.dll", SetLastError=true,
87
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
88
private static extern bool CryptProtectData(
89
ref DATA_BLOB pDataIn,
90
String szDataDescr,
91
ref DATA_BLOB pOptionalEntropy,
92
IntPtr pvReserved,
93
ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
94
int dwFlags,
95
ref DATA_BLOB pDataOut);
96
97
/**//// <summary>
98
///
99
/// </summary>
100
/// <param name="pDataIn"></param>
101
/// <param name="szDataDescr"></param>
102
/// <param name="pOptionalEntropy"></param>
103
/// <param name="pvReserved"></param>
104
/// <param name="pPromptStruct"></param>
105
/// <param name="dwFlags"></param>
106
/// <param name="pDataOut"></param>
107
/// <returns></returns>
108
[DllImport("Crypt32.dll", SetLastError=true,
109
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
110
private static extern bool CryptUnprotectData(
111
ref DATA_BLOB pDataIn,
112
String szDataDescr,
113
ref DATA_BLOB pOptionalEntropy,
114
IntPtr pvReserved,
115
ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
116
int dwFlags,
117
ref DATA_BLOB pDataOut);
118
119
[DllImport("kernel32.dll",
120
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
121
private unsafe static extern int FormatMessage(int dwFlags,
122
ref IntPtr lpSource,
123
int dwMessageId,
124
int dwLanguageId,
125
ref String lpBuffer,
126
int nSize,
127
IntPtr *Arguments);
128
129
/**//// <summary>
130
/// 用于指定创建DataProtector的类型
131
/// </summary>
132
public enum Store
133
{
134
/**//// <summary>
135
/// 机器存储
136
/// </summary>
137
USE_MACHINE_STORE = 1,
138
139
/**//// <summary>
140
/// 用户自己存储
141
/// </summary>
142
USE_USER_STORE
143
};
144
145
/**//// <summary>
146
///
147
/// </summary>
148
private Store m_store;
149
150
/**//// <summary>
151
/// 创建数据保护对象,该对象可能指定数据和处理方法。
152
/// </summary>
153
/// <param name="store">存储类型</param>
154
public DataProtector(Store store)
155
{
156
m_store = store;
157
}
158
159
/**//// <summary>
160
/// 对传入的文本信息进行加密处理,并以字节流方式返回
161
/// </summary>
162
/// <param name="plainText">被字节化的文本</param>
163
/// <param name="optionalEntropy">随机熵,可以为空,如果创建的是用户管理的DataProtector,该选择无效</param>
164
/// <returns>返回被加过密的内容</returns>
165
public byte[] Encrypt(byte[] plainText, byte[] optionalEntropy)
166
{
167
bool retVal = false;
168
DATA_BLOB plainTextBlob = new DATA_BLOB();
169
DATA_BLOB cipherTextBlob = new DATA_BLOB();
170
DATA_BLOB entropyBlob = new DATA_BLOB();
171
CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
172
InitPromptstruct(ref prompt);
173
int dwFlags;
174
try
175
{
176
try
177
{
178
int bytesSize = plainText.Length;
179
plainTextBlob.pbData = Marshal.AllocHGlobal(bytesSize);
180
if(IntPtr.Zero == plainTextBlob.pbData)
181
{
182
throw new Exception(Strings.ALLOC_TEXT_BUFFER_EXCEPTION);
183
}
184
plainTextBlob.cbData = bytesSize;
185
Marshal.Copy(plainText, 0, plainTextBlob.pbData, bytesSize);
186
}
187
catch(Exception ex)
188
{
189
throw new Exception(Strings.MERGING_EXCEPTION + ex.Message);
190
}
191
if(Store.USE_MACHINE_STORE == m_store)
192
{//使用计算机存储,应该提供熵。
193
dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN;
194
//查看熵是否为空
195
if(null == optionalEntropy)
196
{//分配对象
197
optionalEntropy = new byte[0];
198
}
199
try
200
{
201
int bytesSize = optionalEntropy.Length;
202
entropyBlob.pbData = Marshal.AllocHGlobal(optionalEntropy
203
.Length);;
204
if(IntPtr.Zero == entropyBlob.pbData)
205
{
206
throw new Exception(Strings.CANNOT_ALLOC_BUFFER_FOR_ENTROPY);
207
}
208
Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);
209
entropyBlob.cbData = bytesSize;
210
}
211
catch(Exception ex)
212
{
213
214
throw new Exception(Strings.ENTROPY_MERGING_EXCEPTION +
215
ex.Message);
216
}
217
}
218
else
219
{//使用用户存储
220
dwFlags = CRYPTPROTECT_UI_FORBIDDEN;
221
}
222
retVal = CryptProtectData(ref plainTextBlob, "", ref entropyBlob,
223
IntPtr.Zero, ref prompt, dwFlags,
224
ref cipherTextBlob);
225
if(false == retVal)
226
{
227
throw new Exception(Strings.ENCRYPT_FAILED +
228
GetErrorMessage(Marshal.GetLastWin32Error()));
229
}
230
}
231
catch(Exception ex)
232
{
233
throw new Exception(Strings.ENCRYPT_EXCEPTION + ex.Message);
234
}
235
byte[] cipherText = new byte[cipherTextBlob.cbData];
236
Marshal.Copy(cipherTextBlob.pbData, cipherText, 0, cipherTextBlob.cbData);
237
return cipherText;
238
}
239
240
/**//// <summary>
241
/// 对已经加过密的数据进行解密
242
/// </summary>
243
/// <param name="cipherText">密文</param>
244
/// <param name="optionalEntropy">随机熵</param>
245
/// <returns>返回解密后的明文</returns>
246
public byte[] Decrypt(byte[] cipherText, byte[] optionalEntropy)
247
{
248
bool retVal = false;
249
DATA_BLOB plainTextBlob = new DATA_BLOB();
250
DATA_BLOB cipherBlob = new DATA_BLOB();
251
CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
252
InitPromptstruct(ref prompt);
253
try
254
{
255
try
256
{
257
int cipherTextSize = cipherText.Length;
258
cipherBlob.pbData = Marshal.AllocHGlobal(cipherTextSize);
259
if(IntPtr.Zero == cipherBlob.pbData)
260
{
261
throw new Exception(String.Format(Strings.ALLOC_BUFFER_FAILED,"cipherBlob"));
262
}
263
cipherBlob.cbData = cipherTextSize;
264
Marshal.Copy(cipherText, 0, cipherBlob.pbData, cipherBlob.cbData);
265
}
266
catch(Exception ex)
267
{
268
throw new Exception(Strings.MERGING_EXCEPTION + ex.Message);
269
}
270
DATA_BLOB entropyBlob = new DATA_BLOB();
271
int dwFlags;
272
if(Store.USE_MACHINE_STORE == m_store)
273
{//使用计算机存储,应该提供熵。
274
dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN;
275
//查看熵是否为空
276
if(null == optionalEntropy)
277
{//分配对象
278
optionalEntropy = new byte[0];
279
}
280
try
281
{
282
int bytesSize = optionalEntropy.Length;
283
entropyBlob.pbData = Marshal.AllocHGlobal(bytesSize);
284
if(IntPtr.Zero == entropyBlob.pbData)
285
{
286
throw new Exception(String.Format(Strings.ALLOC_BUFFER_FAILED,"熵"));
287
}
288
entropyBlob.cbData = bytesSize;
289
Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);
290
}
291
catch(Exception ex)
292
{
293
throw new Exception("Entropy(熵)"+Strings.MERGING_EXCEPTION +ex.Message);
294
}
295
}
296
else
297
{//使用用户存储
298
dwFlags = CRYPTPROTECT_UI_FORBIDDEN;
299
}
300
retVal = CryptUnprotectData(ref cipherBlob, null, ref entropyBlob,
301
IntPtr.Zero, ref prompt, dwFlags,
302
ref plainTextBlob);
303
if(false == retVal)
304
{
305
throw new Exception(Strings.DECRYPT_FAILED +
306
GetErrorMessage(Marshal.GetLastWin32Error()));
307
}
308
//释放 blob 和熵。
309
if(IntPtr.Zero != cipherBlob.pbData)
310
{
311
Marshal.FreeHGlobal(cipherBlob.pbData);
312
}
313
if(IntPtr.Zero != entropyBlob.pbData)
314
{
315
Marshal.FreeHGlobal(entropyBlob.pbData);
316
}
317
}
318
catch(Exception ex)
319
{
320
throw new Exception(Strings.DECRYPT_EXCEPTION + ex.Message);
321
}
322
byte[] plainText = new byte[plainTextBlob.cbData];
323
Marshal.Copy(plainTextBlob.pbData, plainText, 0, plainTextBlob.cbData);
324
return plainText;
325
}
326
327
/**//// <summary>
328
///
329
/// </summary>
330
/// <param name="ps"></param>
331
private void InitPromptstruct(ref CRYPTPROTECT_PROMPTSTRUCT ps)
332
{
333
ps.cbSize = Marshal.SizeOf(typeof(CRYPTPROTECT_PROMPTSTRUCT));
334
ps.dwPromptFlags = 0;
335
ps.hwndApp = NullPtr;
336
ps.szPrompt = null;
337
}
338
339
/**//// <summary>
340
///
341
/// </summary>
342
/// <param name="errorCode"></param>
343
/// <returns></returns>
344
private unsafe static String GetErrorMessage(int errorCode)
345
{
346
int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
347
int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
348
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
349
int messageSize = 255;
350
String lpMsgBuf = "";
351
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
352
FORMAT_MESSAGE_FROM_SYSTEM |
353
FORMAT_MESSAGE_IGNORE_INSERTS;
354
IntPtr ptrlpSource = new IntPtr();
355
IntPtr prtArguments = new IntPtr();
356
int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0,
357
ref lpMsgBuf, messageSize, &
358
prtArguments);
359
if(0 == retVal)
360
{
361
throw new Exception(Strings.SET_ERROR_MSG_FAILED + errorCode + "。");
362
}
363
return lpMsgBuf;
364
}
365
}
366
internal class Strings
367
{
368
private Strings()
369
{
370
}
371
internal static string ENTROPY_MERGING_EXCEPTION ="熵整理数据时发生异常。";
372
internal static string MERGING_EXCEPTION ="整理数据时发生异常。";
373
internal static string CANNOT_ALLOC_BUFFER_FOR_ENTROPY ="无法分配熵数据缓冲区。";
374
internal static string ALLOC_TEXT_BUFFER_EXCEPTION ="无法分配纯文本缓冲区。";
375
internal static string ALLOC_BUFFER_FAILED ="为{0}分配缓存区失败。";
376
internal static string ENCRYPT_FAILED ="加密失败。";
377
internal static string DECRYPT_FAILED ="解密失败。";
378
internal static string ENCRYPT_EXCEPTION ="加密时发生异常。";
379
internal static string DECRYPT_EXCEPTION ="解密时发生异常。";
380
internal static string SET_ERROR_MSG_FAILED ="无法设置错误代码消息的格式。";
381
internal static string NOT_RIGHT_CONFIGPATH ="不正确的配置文件路径。";
382
internal static string CONFIG_PATH_NOT_SETTED ="配置文件的全路径未设置。";
383
internal static string NOT_FOUND_CONNECTION_STRING ="没有发现配置文件中有此文件。";
384
internal static string CONN_STRING_NAMED_EXCEPTION ="配置文件中定义的数据库连接字符串的key的名字应当以ConnectionString打头。";
385
}
386
}
387
388

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

131

132

133



134


135

136

137

138

139


140

141

142

143

144

145


146

147

148

149

150


151

152

153

154

155



156

157

158

159


160

161

162

163

164

165

166



167

168

169

170

171

172

173

174

175



176

177



178

179

180

181



182

183

184

185

186

187

188



189

190

191

192



193

194

195

196



197

198

199

200



201

202

203

204

205



206

207

208

209

210

211

212



213

214

215

216

217

218

219



220

221

222

223

224

225

226



227

228

229

230

231

232



233

234

235

236

237

238

239

240


241

242

243

244

245

246

247



248

249

250

251

252

253

254



255

256



257

258

259

260



261

262

263

264

265

266

267



268

269

270

271

272

273



274

275

276

277



278

279

280

281



282

283

284

285



286

287

288

289

290

291

292



293

294

295

296

297



298

299

300

301

302

303

304



305

306

307

308

309

310



311

312

313

314



315

316

317

318

319



320

321

322

323

324

325

326

327


328

329

330

331

332



333

334

335

336

337

338

339


340

341

342

343

344

345



346

347

348

349

350

351

352

353

354

355

356

357

358

359

360



361

362

363

364

365

366

367



368

369



370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388
