misc/Create-DPAPI-DLL.ps1
# May need to adjust paths when versions change, and this may need to be pasted into an interactive window. # This will generate a DLL that can be used instead of a code block. # download https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/ and extract with 7-zip to a location, enter that location on the next line $DotNetCodeDomLocation = 'C:\Utils\microsoft.codedom.providers.dotnetcompilerplatform' # Next load it. Add-Type -Path "$DotNetCodeDomLocation\lib\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll" # using Invoke-Expression moves this class definition to runtime, so it will work after the add-type and the ps5 class interface implementation will succeed # This uses the public interface ICompilerSettings instead of the private class CompilerSettings Invoke-Expression -Command @" class RoslynCompilerSettings : Microsoft.CodeDom.Providers.DotNetCompilerPlatform.ICompilerSettings { [string] get_CompilerFullPath() { return "$DotNetCodeDomLocation\tools\Roslyn-4.1.0\csc.exe" } [int] get_CompilerServerTimeToLive() { return 10 } } "@ $DotNetCodeDomProvider = [Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider]::new([RoslynCompilerSettings]::new()) $Code=@" using System; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; public static class DpapiNgUtil { public static string ProtectBase64(string protectionDescriptor, string input) { byte[] output = Protect(protectionDescriptor, Encoding.UTF8.GetBytes(input)); return Convert.ToBase64String(output); } public static string UnprotectBase64(string input) { byte[] bytes = Convert.FromBase64String(input); byte[] output = Unprotect(bytes); return Encoding.UTF8.GetString(output); } public static byte[] Protect(string protectionDescriptor, byte[] data) { using (NCryptProtectionDescriptorHandle handle = NCryptProtectionDescriptorHandle.Create(protectionDescriptor)) { return Protect(handle, data); } } internal static byte[] Protect(NCryptProtectionDescriptorHandle descriptor, byte[] data) { uint cbProtectedBlob; LocalAllocHandle protectedBlobHandle; int status = NativeMethods.NCryptProtectSecret(descriptor, NativeMethods.NCRYPT_SILENT_FLAG, data, (uint)data.Length, IntPtr.Zero, IntPtr.Zero, out protectedBlobHandle, out cbProtectedBlob); if(status != 0) { throw new CryptographicException(status); } using (protectedBlobHandle) { byte[] retVal = new byte[cbProtectedBlob]; Marshal.Copy(protectedBlobHandle.DangerousGetHandle(), retVal, 0, retVal.Length); return retVal; } } public static byte[] Unprotect(byte[] protectedData) { uint cbData; LocalAllocHandle dataHandle; int status = NativeMethods.NCryptUnprotectSecret(IntPtr.Zero, NativeMethods.NCRYPT_SILENT_FLAG, protectedData, (uint)protectedData.Length, IntPtr.Zero, IntPtr.Zero, out dataHandle, out cbData); if (status != 0) { throw new CryptographicException(status); } using (dataHandle) { byte[] retVal = new byte[cbData]; Marshal.Copy(dataHandle.DangerousGetHandle(), retVal, 0, retVal.Length); return retVal; } } } internal class LocalAllocHandle : SafeHandle { // Called by P/Invoke when returning SafeHandles private LocalAllocHandle() : base(IntPtr.Zero, ownsHandle: true) { } // Do not provide a finalizer - SafeHandle's critical finalizer will // call ReleaseHandle for you. public override bool IsInvalid { get { return handle == IntPtr.Zero; } } protected override bool ReleaseHandle() { IntPtr retVal = NativeMethods.LocalFree(handle); return (retVal == IntPtr.Zero); } } internal class NCryptProtectionDescriptorHandle : SafeHandle { // Called by P/Invoke when returning SafeHandles private NCryptProtectionDescriptorHandle() : base(IntPtr.Zero, ownsHandle: true) { } // Do not provide a finalizer - SafeHandle's critical finalizer will // call ReleaseHandle for you. public override bool IsInvalid { get { return handle == IntPtr.Zero; } } public static NCryptProtectionDescriptorHandle Create(string protectionDescriptor) { NCryptProtectionDescriptorHandle descriptorHandle; int status = NativeMethods.NCryptCreateProtectionDescriptor(protectionDescriptor, 0, out descriptorHandle); if (status != 0) { throw new CryptographicException(status); } return descriptorHandle; } protected override bool ReleaseHandle() { int retVal = NativeMethods.NCryptCloseProtectionDescriptor(handle); return (retVal == 0); } } internal static class NativeMethods { private const string KERNEL32LIB = "kernel32.dll"; private const string NCRYPTLIB = "ncrypt.dll"; internal const uint NCRYPT_SILENT_FLAG = 0x00000040; // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366730(v=vs.85).aspx [DllImport(KERNEL32LIB, SetLastError = true)] internal static extern IntPtr LocalFree( [In] IntPtr handle); // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706799(v=vs.85).aspx [DllImport(NCRYPTLIB)] internal extern static int NCryptCloseProtectionDescriptor( [In] IntPtr hDescriptor); // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706800(v=vs.85).aspx [DllImport(NCRYPTLIB, CharSet = CharSet.Unicode)] internal extern static int NCryptCreateProtectionDescriptor( [In] string pwszDescriptorString, [In] uint dwFlags, [Out] out NCryptProtectionDescriptorHandle phDescriptor); // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706802(v=vs.85).aspx [DllImport(NCRYPTLIB)] internal extern static int NCryptProtectSecret( [In] NCryptProtectionDescriptorHandle hDescriptor, [In] uint dwFlags, [In] byte[] pbData, [In] uint cbData, [In] IntPtr pMemPara, [In] IntPtr hWnd, [Out] out LocalAllocHandle ppbProtectedBlob, [Out] out uint pcbProtectedBlob); // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706811(v=vs.85).aspx [DllImport(NCRYPTLIB)] internal extern static int NCryptUnprotectSecret( [In] IntPtr phDescriptor, [In] uint dwFlags, [In] byte[] pbProtectedBlob, [In] uint cbProtectedBlob, [In] IntPtr pMemPara, [In] IntPtr hWnd, [Out] out LocalAllocHandle ppbData, [Out] out uint pcbData); } "@ Add-Type -CodeDomProvider $DotNetCodeDomProvider -TypeDefinition $Code -ReferencedAssemblies @([System.Reflection.Assembly]::GetAssembly([hashtable]).Location) $DotNetAssemblyParameters = [System.CodeDom.Compiler.CompilerParameters]::New( @([System.reflection.assembly]::GetAssembly([hashtable]).Location), ".\dpapi-ng.dll", $false ) $compilationResults = $DotNetCodeDomProvider.CompileAssemblyFromSource( $DotNetAssemblyParameters, $Code ) |