2017-07-07 57 views
1

我正在開發一個程序,用於模擬另一個帳戶,然後使用模擬帳戶的密鑰存儲庫中的私鑰解密文件。訪問模擬帳戶的私鑰

的問題是,雖然我能夠打開模擬的帳戶的證書存儲區和實例化與適當的證書,當程序試圖訪問私鑰的X509Certificate2對象,我收到一個異常:

System.Security.Cryptography.CryptographicException:系統找不到指定的文件......在System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()

我能夠找到模擬用戶的私鑰該文件系統使用FindPrivateKe y.exe實用程序&發現它位於C:\ Users \ TESTUSR \ AppData \ Roaming \ Microsoft \ Crypto \ RSA ...我使用this article作爲指導,以確保我自己的帳戶可以訪問私鑰文件。我可以從NTFS角度訪問密鑰文件,因爲我可以在記事本中加載它(儘管顯然是jiberish)。

非常偶然,我發現程序確實工作,如果我通過使用runas預先加載模擬帳戶的本地配置文件,例如,作爲該用戶打開命令提示符。

任何意見將不勝感激!

class Program 
{ 
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, 
    int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    public extern static bool CloseHandle(IntPtr handle); 

    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] 
    static void Main(string[] args) 
    { 
     const int LOGON32_PROVIDER_DEFAULT = 0; 
     const int LOGON32_LOGON_INTERACTIVE = 2; 

     SafeTokenHandle safeTokenHandle; 

     try 
     { 
      string username = "TESTUSR"; 
      string domain = "CONTOSO"; 
      string password = "[email protected]"; 

      // Call LogonUser to obtain a handle to an access token. 
      if (!LogonUser(username, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle)) 
       return; 

      if (safeTokenHandle == null) 
      { 
       int ret = Marshal.GetLastWin32Error(); 
       throw new System.ComponentModel.Win32Exception(ret); 
      } 
      using (safeTokenHandle) 
      { 
       using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle())) 
       { 
        using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) 
        { 
         string neededCertSN = "17396B080000000000A5"; 
         X509Store my = new X509Store(StoreLocation.CurrentUser); 
         my.Open(OpenFlags.ReadOnly); 

         string result; 
         foreach (X509Certificate2 cert in my.Certificates) 
         { 
          if (Regex.IsMatch(neededCertSN, cert.SerialNumber)) 
          { 
           result = DecryptFile(args[0], ".txt", (RSACryptoServiceProvider)cert.PrivateKey); 
           Console.WriteLine("Result:\r\n" + result); 
           return; 
          } 
         } 

         Console.WriteLine("Encryption certificate not found."); 
        } 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("An exception occurred:\r\n" + ex.ToString()); 
     } 
    } 

    static string DecryptFile(string cypherTextFile, string plainTextFileExtension, RSACryptoServiceProvider rsaPrivateKey) 
    { 
     string plainTextFile = string.Empty; 
     try 
     { 
      if (!File.Exists(cypherTextFile)) 
       return "Cyphertext file not found"; 

      // Create instance of AesManaged for 
      // symetric decryption of the data. 
      using (AesManaged aesManaged = new AesManaged()) 
      { 
       aesManaged.KeySize = 256; 
       aesManaged.BlockSize = 128; 
       aesManaged.Mode = CipherMode.CBC; 

       // Create byte arrays to get the length of 
       // the encrypted key and IV. 
       // These values were stored as 4 bytes each 
       // at the beginning of the encrypted package. 
       byte[] LenK = new byte[4]; 
       byte[] LenIV = new byte[4]; 

       // Consruct the file name for the decrypted file. 
       if (plainTextFileExtension[0] != '.') 
        plainTextFileExtension = "." + plainTextFileExtension; 
       string[] parts = cypherTextFile.Split('.'); 
       for (int x = 0; x < parts.Length - 1; x++) 
        plainTextFile = plainTextFile + parts[x]; 
       plainTextFile = plainTextFile + plainTextFileExtension; 

       // Use FileStream objects to read the encrypted 
       // file (inFs) and save the decrypted file (outFs). 
       using (FileStream inFs = new FileStream(cypherTextFile, FileMode.Open)) 
       { 

        inFs.Seek(0, SeekOrigin.Begin); 
        inFs.Seek(0, SeekOrigin.Begin); 
        inFs.Read(LenK, 0, 3); 
        inFs.Seek(4, SeekOrigin.Begin); 
        inFs.Read(LenIV, 0, 3); 

        // Convert the lengths to integer values. 
        int lenK = BitConverter.ToInt32(LenK, 0); 
        int lenIV = BitConverter.ToInt32(LenIV, 0); 

        // Determine the start postition of 
        // the ciphter text (startC) 
        // and its length(lenC). 
        int startC = lenK + lenIV + 8; 
        int lenC = (int)inFs.Length - startC; 

        // Create the byte arrays for 
        // the encrypted AesManaged key, 
        // the IV, and the cipher text. 
        byte[] KeyEncrypted = new byte[lenK]; 
        byte[] IV = new byte[lenIV]; 

        // Extract the key and IV 
        // starting from index 8 
        // after the length values. 
        inFs.Seek(8, SeekOrigin.Begin); 
        inFs.Read(KeyEncrypted, 0, lenK); 
        inFs.Seek(8 + lenK, SeekOrigin.Begin); 
        inFs.Read(IV, 0, lenIV); 

        // Use RSACryptoServiceProvider 
        // to decrypt the AesManaged key. 
        byte[] KeyDecrypted = rsaPrivateKey.Decrypt(KeyEncrypted, false); 

        // Decrypt the key. 
        using (ICryptoTransform transform = aesManaged.CreateDecryptor(KeyDecrypted, IV)) 
        { 

         // Decrypt the cipher text from 
         // from the FileSteam of the encrypted 
         // file (inFs) into the FileStream 
         // for the decrypted file (outFs). 
         using (FileStream outFs = new FileStream(plainTextFile, FileMode.Create)) 
         { 

          int count = 0; 
          int offset = 0; 

          int blockSizeBytes = aesManaged.BlockSize/8; 
          byte[] data = new byte[blockSizeBytes]; 

          // By decrypting a chunk a time, 
          // you can save memory and 
          // accommodate large files. 

          // Start at the beginning 
          // of the cipher text. 
          inFs.Seek(startC, SeekOrigin.Begin); 
          using (CryptoStream outStreamDecrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write)) 
          { 
           do 
           { 
            count = inFs.Read(data, 0, blockSizeBytes); 
            offset += count; 
            outStreamDecrypted.Write(data, 0, count); 

           } 
           while (count > 0); 

           outStreamDecrypted.FlushFinalBlock(); 
           outStreamDecrypted.Close(); 
          } 
          outFs.Close(); 
         } 
         inFs.Close(); 
        } 
       } 
      } 

      return plainTextFile; 
     } 
     catch (Exception e) 
     { 
      return "An exception occurred:\r\n" + e.ToString(); 
     } 
    } 

} 

public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid 
{ 
    private SafeTokenHandle() 
     : base(true) 
    { 
    } 

    [DllImport("kernel32.dll")] 
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
    [SuppressUnmanagedCodeSecurity] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool CloseHandle(IntPtr handle); 

    protected override bool ReleaseHandle() 
    { 
     return CloseHandle(handle); 
    } 
} 

我遇到的問題是,雖然我能夠打開模擬的帳戶的證書存儲&搶在X509Certificate2對象正確的證書,當程序嘗試使用私鑰,這是

+0

出現您的問題被截斷:*「...程序嘗試使用私鑰,這是」 *。我猜想其中一個突出的問題是,LogonUser在p/invoke'd時加載用戶的註冊表配置單元。另請參閱[Windows用戶模擬有哪些副作用?](https://stackoverflow.com/q/28727265/608639)和[模擬和當前用戶註冊表訪問?](https://stackoverflow.com/q/4392221/608639) – jww

回答

1

您需要根據CreateProcessAsUser文件註冊表來手動加載用戶:

CreateProcessAsUser不指定用戶的配置文件加載到HKEY_USERS註冊表項。因此,要訪問HKEY_CURRENT_USER註冊表項中的信息,必須在調用CreateProcessAsUser之前使用LoadUserProfile函數將用戶的配置文件信息加載到HKEY_USERS中。

RunAs不會在您的進程中更改HKEY_CURRENT_USER,但會導致配置單元在HKEY_USERS下加載。

你可以嘗試LogonUser後調用LoadUserProfile ...

+0

好的 - 所以,將證書綁定到文件系統中的私鑰材料的信息實際上存儲在註冊表中。這就說得通了!我將通過無法訪問某種DPAPI加密密鑰來解決這個問題。 :P 我會試試這個,讓你知道! –