• | 创建 Visual C# 类库
1. | 启动 Visual Studio .NET,并创建一个名为 DataProtection 的新 Visual C# 类库项目。 | 2. | 使用解决方案资源管理器将 class1.cs 重命名为 DataProtection.cs。 | 3. | 在 DataProtection.cs 中,将 Class1 重命名为 DataProtector 并相应地重命名默认的构造函数。 | 4. | 在解决方案资源管理器中,右键单击“DataProtection”,然后单击“属性”。 | 5. | 单击“配置属性”文件夹,并将“允许不安全代码块”设置为“True”。 | 6. | 单击“确定”以关闭“属性”对话框。 | 7. | 将下面的 using 语句添加到 DataProtection.cs 顶部现有 using 语句的下面: using System.Text; using System.Runtime.InteropServices;
| 8. | 将下面的 DllImport 语句添加到 DataProtector 类的上面,以便通过 P/Invoke 调用 Win32 DPAPI 函数和 FormatMessage 实用程序函数: [DllImport("Crypt32.dll", SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] private static extern bool CryptProtectData( ref DATA_BLOB pDataIn, String szDataDescr, ref DATA_BLOB pOptionalEntropy, IntPtr pvReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct, int dwFlags, ref DATA_BLOB pDataOut); [DllImport("Crypt32.dll", SetLastError=true,CharSet=System.Runtime.InteropServices.CharSet.Auto)] private static extern bool CryptUnprotectData( ref DATA_BLOB pDataIn, String szDataDescr, ref DATA_BLOB pOptionalEntropy, IntPtr pvReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct, int dwFlags, ref DATA_BLOB pDataOut); [DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)] private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr *Arguments); | 9. | 添加 DPAPI 函数使用的以下结构定义和常量: [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] internal struct DATA_BLOB { public int cbData; public IntPtr pbData; }
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] internal struct CRYPTPROTECT_PROMPTSTRUCT { public int cbSize; public int dwPromptFlags; public IntPtr hwndApp; public String szPrompt; } static private IntPtr NullPtr = ((IntPtr)((int)(0))); private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1; private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;
| 10. | 将名为 Store 的公用枚举类型添加到该类中。它用于表示是应该将 DPAPI 与计算机存储结合使用,还是与用户存储结合使用。 public enum Store {USE_MACHINE_STORE = 1, USE_USER_STORE};
| 11. | 将类型为 Store 的专用成员变量添加到该类中。 private Store store;
| 12. | 使用以下构造函数替换该类的默认构造函数,以接受 Store 参数并将提供的值放在 Store 专用成员变量中: public DataProtector(Store tempStore) { store = tempStore; }
| 13. | 将下面的公用 Encrypt 方法添加到该类中: public byte[] Encrypt(byte[] plainText, byte[] optionalEntropy) { bool retVal = false;
DATA_BLOB plainTextBlob = new DATA_BLOB(); DATA_BLOB cipherTextBlob = new DATA_BLOB(); DATA_BLOB entropyBlob = new DATA_BLOB();
CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT(); InitPromptstruct(ref prompt);
int dwFlags; try { try { int bytesSize = plainText.Length; plainTextBlob.pbData = Marshal.AllocHGlobal(bytesSize); if(IntPtr.Zero == plainTextBlob.pbData) { throw new Exception("无法分配纯文本缓冲区。"); } plainTextBlob.cbData = bytesSize; Marshal.Copy(plainText, 0, plainTextBlob.pbData, bytesSize); } catch(Exception ex) { throw new Exception("整理数据时发生异常。 " + ex.Message); } if(Store.USE_MACHINE_STORE == store) {//使用计算机存储,应该提供熵。 dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN; //查看熵是否为空 if(null == optionalEntropy) {//分配对象 optionalEntropy = new byte[0]; } try { int bytesSize = optionalEntropy.Length; entropyBlob.pbData = Marshal.AllocHGlobal(optionalEntropy .Length);; if(IntPtr.Zero == entropyBlob.pbData) { throw new Exception("无法分配熵数据缓冲区。"); } Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize); entropyBlob.cbData = bytesSize; } catch(Exception ex) { throw new Exception("熵整理数据时发生异常。 " + ex.Message); } } else {//使用用户存储 dwFlags = CRYPTPROTECT_UI_FORBIDDEN; } retVal = CryptProtectData(ref plainTextBlob, "", ref entropyBlob, IntPtr.Zero, ref prompt, dwFlags, ref cipherTextBlob); if(false == retVal) { throw new Exception("加密失败。 " + GetErrorMessage(Marshal.GetLastWin32Error())); } } catch(Exception ex) { throw new Exception("加密时发生异常。 " + ex.Message); } byte[] cipherText = new byte[cipherTextBlob.cbData]; Marshal.Copy(cipherTextBlob.pbData, cipherText, 0, cipherTextBlob .cbData); return cipherText; }
| 14. | 将下面的公用 Decrypt 方法添加到该类中: public byte[] Decrypt(byte[] cipherText, byte[] optionalEntropy) { bool retVal = false; DATA_BLOB plainTextBlob = new DATA_BLOB(); DATA_BLOB cipherBlob = new DATA_BLOB(); CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT(); InitPromptstruct(ref prompt); try { try { int cipherTextSize = cipherText.Length; cipherBlob.pbData = Marshal.AllocHGlobal(cipherTextSize); if(IntPtr.Zero == cipherBlob.pbData) { throw new Exception("无法分配 cipherText 缓冲区。"); } cipherBlob.cbData = cipherTextSize; Marshal.Copy(cipherText, 0, cipherBlob.pbData, cipherBlob.cbData); } catch(Exception ex) { throw new Exception("整理数据时发生异常。 " + ex.Message); } DATA_BLOB entropyBlob = new DATA_BLOB(); int dwFlags; if(Store.USE_MACHINE_STORE == store) {//使用计算机存储,应该提供熵。 dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN; //查看熵是否为空 if(null == optionalEntropy) {//分配对象 optionalEntropy = new byte[0]; } try { int bytesSize = optionalEntropy.Length; entropyBlob.pbData = Marshal.AllocHGlobal(bytesSize); if(IntPtr.Zero == entropyBlob.pbData) { throw new Exception("无法分配熵缓冲区。"); } entropyBlob.cbData = bytesSize; Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize); } catch(Exception ex) { throw new Exception("熵整理数据时发生异常。 " + ex.Message); } } else {//使用用户存储 dwFlags = CRYPTPROTECT_UI_FORBIDDEN; } retVal = CryptUnprotectData(ref cipherBlob, null, ref entropyBlob, IntPtr.Zero, ref prompt, dwFlags, ref plainTextBlob); if(false == retVal) { throw new Exception("解密失败。 " + GetErrorMessage(Marshal.GetLastWin32Error())); } //释放 blob 和熵。 if(IntPtr.Zero != cipherBlob.pbData) { Marshal.FreeHGlobal(cipherBlob.pbData); } if(IntPtr.Zero != entropyBlob.pbData) { Marshal.FreeHGlobal(entropyBlob.pbData); } } catch(Exception ex) { throw new Exception("解密时发生异常。 " + ex.Message); } byte[] plainText = new byte[plainTextBlob.cbData]; Marshal.Copy(plainTextBlob.pbData, plainText, 0, plainTextBlob.cbData); return plainText; }
| 15. | 将下面的私有帮助器方法添加到该类中: private void InitPromptstruct(ref CRYPTPROTECT_PROMPTSTRUCT ps) { ps.cbSize = Marshal.SizeOf(typeof(CRYPTPROTECT_PROMPTSTRUCT)); ps.dwPromptFlags = 0; ps.hwndApp = NullPtr; ps.szPrompt = null; }
private unsafe static String GetErrorMessage(int errorCode) { int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; int messageSize = 255; String lpMsgBuf = ""; int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; IntPtr ptrlpSource = new IntPtr(); IntPtr prtArguments = new IntPtr(); int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, & prtArguments); if(0 == retVal) { throw new Exception("无法设置错误代码消息的格式 " + errorCode + ". "); } return lpMsgBuf; }
| 16. | 在“生成”菜单中,单击“生成解决方案”。 | |