2013-10-10 34 views
3

我想從C#代碼中使用CryptoAPI將SHA256時間戳添加到已簽名的程序集。下面是我使用的代碼:CryptoAPI的SignerTimeStampEx2使用PInvoke

Signer.TimestampSignedAssembly("MyAssembly.exe", "http://tsa.starfieldtech.com"); 

簽名者類:

public static class Signer 
{ 
    [StructLayoutAttribute(LayoutKind.Sequential)] 
    struct SIGNER_SUBJECT_INFO 
    { 
     public uint cbSize; 
     public IntPtr pdwIndex; 
     public uint dwSubjectChoice; 
     public SubjectChoiceUnion Union1; 
     [StructLayoutAttribute(LayoutKind.Explicit)] 
     internal struct SubjectChoiceUnion 
     { 
      [FieldOffsetAttribute(0)] 
      public IntPtr pSignerFileInfo; 
      [FieldOffsetAttribute(0)] 
      public IntPtr pSignerBlobInfo; 
     } 
    } 

    [StructLayoutAttribute(LayoutKind.Sequential)] 
    struct SIGNER_FILE_INFO 
    { 
     public uint cbSize; 
     public IntPtr pwszFileName; 
     public IntPtr hFile; 
    } 

    [DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
    private static extern int SignerTimeStampEx2(
     uint dwFlags,    // DWORD 
     IntPtr pSubjectInfo,  // SIGNER_SUBJECT_INFO 
     string pwszHttpTimeStamp, // LPCWSTR 
     uint dwAlgId,    // ALG_ID 
     IntPtr psRequest,   // PCRYPT_ATTRIBUTES 
     IntPtr pSipData,   // LPVOID 
     out IntPtr ppSignerContext // SIGNER_CONTEXT 
     ); 

    public static void TimestampSignedAssembly(string appPath, string tsaServer) 
    { 
     if (tsaServer == null) throw new ArgumentNullException("tsaServer"); 

     var pSubjectInfo = IntPtr.Zero;    
     try 
     {     
      pSubjectInfo = CreateSignerSubjectInfo(appPath); 
      TimestampSignedAssembly(pSubjectInfo, tsaServer); 
     } 
     finally 
     {     
      if (pSubjectInfo != IntPtr.Zero) 
      { 
       Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO)); 
      }     
     } 
    } 

    private static IntPtr CreateSignerSubjectInfo(string pathToAssembly) 
    { 
     var info = new SIGNER_SUBJECT_INFO 
     { 
      cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)), 
      pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint))) 
     }; 
     var index = 0; 
     Marshal.StructureToPtr(index, info.pdwIndex, false); 

     info.dwSubjectChoice = 0x1; //SIGNER_SUBJECT_FILE 
     var assemblyFilePtr = Marshal.StringToHGlobalUni(pathToAssembly); 

     var fileInfo = new SIGNER_FILE_INFO 
     { 
      cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)), 
      pwszFileName = assemblyFilePtr, 
      hFile = IntPtr.Zero 
     }; 

     info.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion 
     { 
      pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO))) 
     }; 

     Marshal.StructureToPtr(fileInfo, info.Union1.pSignerFileInfo, false); 

     IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(info)); 
     Marshal.StructureToPtr(info, pSubjectInfo, false); 

     return pSubjectInfo; 
    } 

    /* 
     Here CryptoAPI function SignerTimeStampEx2 called. 
    */ 
    private static void TimestampSignedAssembly(IntPtr pSubjectInfo, string tsaServer) 
    {    
     IntPtr context; 
     var hResult = SignerTimeStampEx2(
      0x1,   // I have not found anywhere what value should have this parameter! 
      pSubjectInfo, 
      tsaServer,  
      0x0000800c,  // 256 bit SHA hashing algorithm. This value taken form here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx 
      IntPtr.Zero,  
      IntPtr.Zero, 
      out context 
      ); 

     if (hResult != 0) 
     { 
      throw new Exception(string.Format("Error occured when adding timestamp - Error code: 0x{0:X}", hResult)); 
     } 
    } 
} 

儘管我傳遞給SignerTimeStampEx2功能的參數(dwAlgId),這表明有必要增加SHA256時間戳(0x0000800c),始終會生成SHA1時間戳。

有沒有人遇到過這個問題?我做錯了什麼?我應該設置什麼值dwFlagsdwAlgId參數?

提前致謝!

+1

+1。我認爲你應該使用SIGNER_TIMESTAMP_RFC3161作爲第一個標誌。現在...我不知道在哪裏可以找到SIGNER_TIMESTAMP_RFC3161和SIGNER_TIMESTAMP_AUTHENTICODE值...在反彙編mssign32.dll中,我認爲'1'意味着SIGNER_TIMESTAMP_AUTHENTICODE(因爲SignerTimeStamp調用SignerTimeStampEx3,第一個參數值爲1)。所以你應該嘗試0,2或3作爲標誌值。 –

+0

感謝您的回答。我試圖傳遞0,2和3作爲標誌值,並得到以下結果:0和3 - 0x80070057錯誤發生(一個或多個參數無效),2 - crypt32.dll中的APPCRASH(錯誤代碼 - c0000005,訪問衝突?) – ruslangil

+0

所以它的進展:-)你也可以在SIGNER_FILE_INFO中修改pwszFileName。只需使用一個字符串並將CharSet = CharSet.Unicode添加到結構定義中即可。 –

回答

6

dwFlags需要SIGNER_TIMESTAMP_RFC3161(2)。您訪問衝突的原因是SignerTimeStampEx2()錯誤地爲documented。它期望算法是一個PCSTR而不是一個DWORD。如果你通過0x800C它會嘗試解除引用作爲指針,導致AV。所以用PCSTR pszTimeStampAlgorithmOid替換函數聲明中的ALG_ID dwAlgId。將szOID_NIST_sha256傳遞給它,它應該定義爲「2.16.840.1.101.3.4.2.1」。

SignerTimeStampEx3()也錯誤地錯誤documented。 pszTimeStampAlgorithmOid應聲明爲PCSTR而不是PCWSTR。

根據我的經驗,如果在SIGNER_FILE_INFO結構中同時指定文件名和打開的Win32文件句柄,則代碼簽名和時間戳更加可靠。

無論你真的會得到一個SHA-256時間戳還取決於你正在使用的時間戳服務。 http://tsa.starfieldtech.com,http://timestamp.globalsign.com/http://timestamp.comodoca.com/rfc3161問題SHA-256時間戳。即使請求SHA-256時間戳,其他服務也可能發出SHA-1時間戳。

+0

我只是碰到類似的問題,實際上SignerTimeStampEx2 *可以*用於簽名文件,但是SignerTimeStampEx2的API文檔是錯誤的,而不是以ALG_ID作爲參數,它也需要帶時間戳算法的PCSTR oid,正如你所提到的,對於sha256來說是「2.16.840 ....」。 – Imron

+0

問題是我想簽名不是文件,而是一個blob,它總是失敗......應該傳遞哪個guid? – Michael

0

我終於搞定了。這裏是戳器類的完整代碼:

public static class Timestamper 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    struct SIGNER_SUBJECT_INFO 
    { 
     public uint cbSize; 
     public IntPtr pdwIndex; 
     public uint dwSubjectChoice; 
     public SubjectChoiceUnion Union1; 
     [StructLayoutAttribute(LayoutKind.Explicit)] 
     internal struct SubjectChoiceUnion 
     { 
      [FieldOffsetAttribute(0)] 
      public IntPtr pSignerFileInfo; 
      [FieldOffsetAttribute(0)] 
      public IntPtr pSignerBlobInfo; 
     } 
    } 

    [StructLayoutAttribute(LayoutKind.Sequential)] 
    struct SIGNER_FILE_INFO 
    { 
     public uint cbSize; 
     public IntPtr pwszFileName; 
     public IntPtr hFile; 
    } 

    [DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
    private static extern int SignerTimeStampEx2(
     uint dwFlags,     // DWORD 
     IntPtr pSubjectInfo,    // SIGNER_SUBJECT_INFO 
     string pwszHttpTimeStamp,  // LPCWSTR 
     IntPtr pszTimeStampAlgorithmOid, // PCSTR 
     IntPtr psRequest,    // PCRYPT_ATTRIBUTES 
     IntPtr pSipData,     // LPVOID 
     out IntPtr ppSignerContext  // SIGNER_CONTEXT 
    ); 

    public static void TimestampSignedAssembly(string appPath, string tsaServer) 
    { 
     if (tsaServer == null) throw new ArgumentNullException("tsaServer"); 

     IntPtr pSubjectInfo = IntPtr.Zero; 
     try 
     { 
      pSubjectInfo = CreateSignerSubjectInfo(appPath); 
      TimestampSignedAssembly(pSubjectInfo, tsaServer); 
     } 
     finally 
     { 
      if (pSubjectInfo != IntPtr.Zero) 
      { 
       Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO)); 
      } 
     } 
    } 

    private static IntPtr CreateSignerSubjectInfo(string pathToAssembly) 
    { 
     SIGNER_SUBJECT_INFO info = new SIGNER_SUBJECT_INFO 
     { 
      cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)), 
      pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint))) 
     }; 

     int index = 0; 
     Marshal.StructureToPtr(index, info.pdwIndex, false); 

     info.dwSubjectChoice = 0x1; //SIGNER_SUBJECT_FILE 
     IntPtr assemblyFilePtr = Marshal.StringToHGlobalUni(pathToAssembly); 

     SIGNER_FILE_INFO fileInfo = new SIGNER_FILE_INFO 
     { 
      cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)), 
      pwszFileName = assemblyFilePtr, 
      hFile = IntPtr.Zero 
     }; 

     info.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion 
     { 
      pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO))) 
     }; 

     Marshal.StructureToPtr(fileInfo, info.Union1.pSignerFileInfo, false); 

     IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(info)); 
     Marshal.StructureToPtr(info, pSubjectInfo, false); 

     return pSubjectInfo; 
    } 

    /* 
     Here CryptoAPI function SignerTimeStampEx2 called. 
    */ 
    private static void TimestampSignedAssembly(IntPtr pSubjectInfo, string tsaServer) 
    { 
     IntPtr context; 
     int hResult = SignerTimeStampEx2(
      0x2, // SIGNER_TIMESTAMP_RFC3161 
      pSubjectInfo, 
      tsaServer, 
      Marshal.StringToHGlobalAnsi("2.16.840.1.101.3.4.2.1"), // szOID_NIST_sha256 constant, SHA256 hashing algorithm. 
      IntPtr.Zero, 
      IntPtr.Zero, 
      out context 
     ); 

     if (hResult != 0) 
     { 
      throw new Exception(string.Format("Error occured when adding timestamp - Error code: 0x{0:X}", hResult)); 
     } 
    } 
} 

用例:

Timestamper.TimestampSignedAssembly("Assembly.exe", "http://timestamp.comodoca.com/?td=sha256");