/// <summary> /// Protect /// </summary> public static byte[] Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope) { if (userData == null) { throw new ArgumentNullException("userData"); } GCHandle pbDataIn = new GCHandle(); GCHandle pOptionalEntropy = new GCHandle(); CAPI.CRYPTOAPI_BLOB blob = new CAPI.CRYPTOAPI_BLOB(); try { pbDataIn = GCHandle.Alloc(userData, GCHandleType.Pinned); CAPI.CRYPTOAPI_BLOB dataIn = new CAPI.CRYPTOAPI_BLOB(); dataIn.cbData = (uint)userData.Length; dataIn.pbData = pbDataIn.AddrOfPinnedObject(); CAPI.CRYPTOAPI_BLOB entropy = new CAPI.CRYPTOAPI_BLOB(); if (optionalEntropy != null) { pOptionalEntropy = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned); entropy.cbData = (uint)optionalEntropy.Length; entropy.pbData = pOptionalEntropy.AddrOfPinnedObject(); } uint dwFlags = CAPI.CRYPTPROTECT_UI_FORBIDDEN; if (scope == DataProtectionScope.LocalMachine) { dwFlags |= CAPI.CRYPTPROTECT_LOCAL_MACHINE; } unsafe { if (!CAPI.CryptProtectData(new IntPtr(&dataIn), String.Empty, new IntPtr(&entropy), IntPtr.Zero, IntPtr.Zero, dwFlags, new IntPtr(&blob))) { int lastWin32Error = Marshal.GetLastWin32Error(); // One of the most common reasons that DPAPI operations fail is that the user // profile is not loaded (for instance in the case of impersonation or running in a // service. In those cases, throw an exception that provides more specific details // about what happened. if (CAPI.ErrorMayBeCausedByUnloadedProfile(lastWin32Error)) { throw new CryptographicException("Cryptography_DpApi_ProfileMayNotBeLoaded"); } else { throw new CryptographicException(lastWin32Error); } } } // In some cases, the API would fail due to OOM but simply return a null pointer. if (blob.pbData == IntPtr.Zero) { throw new OutOfMemoryException(); } byte[] encryptedData = new byte[(int)blob.cbData]; Marshal.Copy(blob.pbData, encryptedData, 0, encryptedData.Length); return(encryptedData); } finally { if (pbDataIn.IsAllocated) { pbDataIn.Free(); } if (pOptionalEntropy.IsAllocated) { pOptionalEntropy.Free(); } if (blob.pbData != IntPtr.Zero) { CAPI.ZeroMemory(blob.pbData, blob.cbData); CAPI.LocalFree(blob.pbData); } } }
/// <summary> /// Unprotect /// </summary> public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope) { #if UNIX throw new PlatformNotSupportedException(Serialization.DeserializeSecureStringNotSupported); #else if (encryptedData == null) { throw new ArgumentNullException("encryptedData"); } GCHandle pbDataIn = new GCHandle(); GCHandle pOptionalEntropy = new GCHandle(); CAPI.CRYPTOAPI_BLOB userData = new CAPI.CRYPTOAPI_BLOB(); try { pbDataIn = GCHandle.Alloc(encryptedData, GCHandleType.Pinned); CAPI.CRYPTOAPI_BLOB dataIn = new CAPI.CRYPTOAPI_BLOB(); dataIn.cbData = (uint)encryptedData.Length; dataIn.pbData = pbDataIn.AddrOfPinnedObject(); CAPI.CRYPTOAPI_BLOB entropy = new CAPI.CRYPTOAPI_BLOB(); if (optionalEntropy != null) { pOptionalEntropy = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned); entropy.cbData = (uint)optionalEntropy.Length; entropy.pbData = pOptionalEntropy.AddrOfPinnedObject(); } uint dwFlags = CAPI.CRYPTPROTECT_UI_FORBIDDEN; if (scope == DataProtectionScope.LocalMachine) { dwFlags |= CAPI.CRYPTPROTECT_LOCAL_MACHINE; } unsafe { if (!CAPI.CryptUnprotectData(new IntPtr(&dataIn), IntPtr.Zero, new IntPtr(&entropy), IntPtr.Zero, IntPtr.Zero, dwFlags, new IntPtr(&userData))) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } // In some cases, the API would fail due to OOM but simply return a null pointer. if (userData.pbData == IntPtr.Zero) { throw new OutOfMemoryException(); } byte[] data = new byte[(int)userData.cbData]; Marshal.Copy(userData.pbData, data, 0, data.Length); return(data); } finally { if (pbDataIn.IsAllocated) { pbDataIn.Free(); } if (pOptionalEntropy.IsAllocated) { pOptionalEntropy.Free(); } if (userData.pbData != IntPtr.Zero) { CAPI.ZeroMemory(userData.pbData, userData.cbData); CAPI.LocalFree(userData.pbData); } } #endif }
/// <summary> /// Unprotect. /// </summary> public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope) { if (encryptedData is null) { throw new ArgumentNullException(nameof(encryptedData)); } GCHandle pbDataIn = new GCHandle(); GCHandle pOptionalEntropy = new GCHandle(); CAPI.CRYPTOAPI_BLOB userData = new CAPI.CRYPTOAPI_BLOB(); try { pbDataIn = GCHandle.Alloc(encryptedData, GCHandleType.Pinned); CAPI.CRYPTOAPI_BLOB dataIn = new CAPI.CRYPTOAPI_BLOB(); dataIn.cbData = (uint)encryptedData.Length; dataIn.pbData = pbDataIn.AddrOfPinnedObject(); CAPI.CRYPTOAPI_BLOB entropy = new CAPI.CRYPTOAPI_BLOB(); if (optionalEntropy != null) { pOptionalEntropy = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned); entropy.cbData = (uint)optionalEntropy.Length; entropy.pbData = pOptionalEntropy.AddrOfPinnedObject(); } uint dwFlags = CAPI.CRYPTPROTECT_UI_FORBIDDEN; if (scope == DataProtectionScope.LocalMachine) { dwFlags |= CAPI.CRYPTPROTECT_LOCAL_MACHINE; } unsafe { if (!CAPI.CryptUnprotectData( pDataIn: new IntPtr(&dataIn), ppszDataDescr: IntPtr.Zero, pOptionalEntropy: new IntPtr(&entropy), pvReserved: IntPtr.Zero, pPromptStruct: IntPtr.Zero, dwFlags: dwFlags, pDataBlob: new IntPtr(&userData))) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } // In some cases, the API would fail due to OOM but simply return a null pointer. if (userData.pbData == IntPtr.Zero) { throw new OutOfMemoryException(); } byte[] data = new byte[(int)userData.cbData]; Marshal.Copy(userData.pbData, data, 0, data.Length); return(data); } finally { if (pbDataIn.IsAllocated) { pbDataIn.Free(); } if (pOptionalEntropy.IsAllocated) { pOptionalEntropy.Free(); } if (userData.pbData != IntPtr.Zero) { CAPI.ZeroMemory(userData.pbData, userData.cbData); CAPI.LocalFree(userData.pbData); } } }