/// <summary> /// See <see cref="ProtectedData"/>. /// </summary> /// <param name="encryptedData">Data to unprotect.</param> /// <param name="optionalEntropy">Additional material to be added to the symmetric key used for encryption.</param> /// <param name="scope">See <see cref="DataProtectionScope"/>.</param> /// <returns>Plain text data.</returns> /// <exception cref="ArgumentNullException">The specified parameter is a null reference (Nothing in Visual Basic).</exception> /// <exception cref="CryptographicException">Exception when executing unmanaged code.</exception> /// <permission cref="CryptographicPermission">Demand for decryption permission.</permission> public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope) { // Verify input if (encryptedData == null || encryptedData.Length == 0) { throw new ArgumentNullException("encryptedData"); } if (!IsValidScope(scope)) { throw new ArgumentException(Resource.ResourceManager[Resource.MessageKey.InvalidDataProtectionScope]); } // Demand Permission new CryptographicPermission(CryptographicPermissionFlags.Decrypt).Demand(); // Assert UnamanagedCode (replaced by the SuppressUnmanagedCodeSecurity attribute of the Win32Native class // Initialize vars. // Check to see if the entropy is null if (optionalEntropy == null) { // Allocate something optionalEntropy = new byte[0]; } Win32Native.CRYPTOAPI_BLOB plainTextBlob = new Win32Native.CRYPTOAPI_BLOB(); GCHandle encryptedDataHandle = GCHandle.Alloc(encryptedData, GCHandleType.Pinned); GCHandle entropyHandle = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned); try { Win32Native.CRYPTPROTECT_PROMPTSTRUCT prompt = new Win32Native.CRYPTPROTECT_PROMPTSTRUCT(0); Win32Native.CRYPTOAPI_BLOB encryptedDataBlob = new Win32Native.CRYPTOAPI_BLOB(); encryptedDataBlob.cbData = (uint)encryptedData.Length; encryptedDataBlob.pbData = encryptedDataHandle.AddrOfPinnedObject(); Win32Native.CRYPTOAPI_BLOB entropyBlob = new Win32Native.CRYPTOAPI_BLOB(); entropyBlob.cbData = (uint)optionalEntropy.Length; entropyBlob.pbData = entropyHandle.AddrOfPinnedObject(); if (!Win32Native.CryptUnprotectData(ref encryptedDataBlob, null, ref entropyBlob, IntPtr.Zero, ref prompt, (uint)scope, ref plainTextBlob)) { throw new CryptographicException(Resource.ResourceManager[Resource.MessageKey.UnprotectDataException], new Win32Exception(Marshal.GetLastWin32Error())); } if (plainTextBlob.pbData == IntPtr.Zero) { throw new OutOfMemoryException(Resource.ResourceManager[Resource.MessageKey.OutOfMemoryException, "plainTextBlob"]); } byte[] plainText = new byte[plainTextBlob.cbData]; Marshal.Copy(plainTextBlob.pbData, plainText, 0, plainText.Length); return(plainText); } finally { // Free handles if (encryptedDataHandle.IsAllocated) { encryptedDataHandle.Free(); } if (entropyHandle.IsAllocated) { entropyHandle.Free(); } //Free the unmanaged resource ... // Free and erase the plainTextBlob if (plainTextBlob.pbData != IntPtr.Zero) { Win32Native.ZeroMemory(plainTextBlob.pbData, plainTextBlob.cbData); Win32Native.LocalFree(plainTextBlob.pbData); } } }
/// <summary> /// This function performs encryption on the data in a byte array input data. /// If <see cref="DataProtectionScope.CurrentUser"/> is specified, only a user /// with the same logon credentials as the encrypter can decrypt the data. /// <b>Warning</b>If the logon credentials are lost or forgotten, the data is usually unrecoverable. /// If the <see cref="DataProtectionScope.LocalMachine"/> is used, the encryption and decryption must be done on the same computer /// therefore any user on the same computer where the data was encrypted can recover the data. /// </summary> /// <remarks> /// The function creates a session key to perform the encryption. /// The session key is rederived when the data is to be decrypted. /// The function also adds a message authentication code (MAC), which is a keyed integrity check, to the encrypted data to guard against data tampering. /// </remarks> /// <param name="userData">Data to be protected. This array should be erased after use with <see cref="Array.Clear"/>.</param> /// <param name="optionalEntropy">Additional material to be added to the symmetric key used for encryption.</param> /// <param name="scope">See <see cref="DataProtectionScope"/>.</param> /// <returns>Protected data.</returns> /// <exception cref="ArgumentNullException">The specified parameter is a null reference (Nothing in Visual Basic).</exception> /// <exception cref="CryptographicException">Exception when executing unmanaged code.</exception> /// <permission cref="CryptographicPermission">Demand for encryption permission.</permission> public static byte[] Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope) { // Verify input if (userData == null) { throw new ArgumentNullException("userData"); } if (!IsValidScope(scope)) { throw new ArgumentException(Resource.ResourceManager[Resource.MessageKey.InvalidDataProtectionScope]); } // Demand Permission new CryptographicPermission(CryptographicPermissionFlags.Encrypt).Demand(); // Assert UnamanagedCode (replaced by the SuppressUnmanagedCodeSecurity attribute of the Win32Native class // Check to see if the entropy is null if (optionalEntropy == null) { // Allocate something optionalEntropy = new byte[0]; } Win32Native.CRYPTOAPI_BLOB cipherTextBlob = new Win32Native.CRYPTOAPI_BLOB(); // This prevents the garbage collector from moving the object and hence // undermines the efficiency of the garbage collector. We 'll release // the reference on the finally block. GCHandle userDataHandle = GCHandle.Alloc(userData, GCHandleType.Pinned); GCHandle entropyHandle = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned); try { Win32Native.CRYPTPROTECT_PROMPTSTRUCT prompt = new Win32Native.CRYPTPROTECT_PROMPTSTRUCT(0); Win32Native.CRYPTOAPI_BLOB plainTextBlob = new Win32Native.CRYPTOAPI_BLOB(); plainTextBlob.cbData = (uint)userData.Length; plainTextBlob.pbData = userDataHandle.AddrOfPinnedObject(); Win32Native.CRYPTOAPI_BLOB entropyBlob = new Win32Native.CRYPTOAPI_BLOB(); entropyBlob.cbData = (uint)optionalEntropy.Length; entropyBlob.pbData = entropyHandle.AddrOfPinnedObject(); if (!Win32Native.CryptProtectData(ref plainTextBlob, null, ref entropyBlob, IntPtr.Zero, ref prompt, (uint)scope, ref cipherTextBlob)) { throw new CryptographicException(Resource.ResourceManager[Resource.MessageKey.ProtectDataException], new Win32Exception(Marshal.GetLastWin32Error())); } //Check returned data if (cipherTextBlob.pbData == IntPtr.Zero) { throw new OutOfMemoryException(Resource.ResourceManager[Resource.MessageKey.OutOfMemoryException, "cipherTextBlob"]); } // Move encrypted data to the returned array byte[] cipherText = new byte[cipherTextBlob.cbData]; Marshal.Copy(cipherTextBlob.pbData, cipherText, 0, cipherText.Length); return(cipherText); } finally { // Free the allocated handles if (userDataHandle.IsAllocated) { userDataHandle.Free(); } if (entropyHandle.IsAllocated) { entropyHandle.Free(); } //Free the unmanaged resource ... // Free and erase the cipherTextBlob if (cipherTextBlob.pbData != IntPtr.Zero) { Win32Native.ZeroMemory(cipherTextBlob.pbData, cipherTextBlob.cbData); Win32Native.LocalFree(cipherTextBlob.pbData); } } }