Skip to content

Commit e836752

Browse files
authored
Create DumpCertificateInfo.cs
1 parent 5a8dc01 commit e836752

File tree

1 file changed

+219
-0
lines changed

1 file changed

+219
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*********************************************************************************
2+
* MIT License *
3+
* *
4+
* Copyright (c) 2022 Jason *
5+
* *
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy *
7+
* of this software and associated documentation files (the "Software"), to deal *
8+
* in the Software without restriction, including without limitation the rights *
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *
10+
* copies of the Software, and to permit persons to whom the Software is *
11+
* furnished to do so, subject to the following conditions: *
12+
* *
13+
* The above copyright notice and this permission notice shall be included in all *
14+
* copies or substantial portions of the Software. *
15+
* *
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE *
22+
* SOFTWARE. *
23+
**********************************************************************************/
24+
25+
using Microsoft.Win32.SafeHandles;
26+
using System;
27+
using System.IO;
28+
using System.Runtime.InteropServices;
29+
using System.Security.Cryptography.X509Certificates;
30+
31+
namespace DumpCertificateInfo
32+
{
33+
internal class Program
34+
{
35+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
36+
public struct CATALOG_INFO
37+
{
38+
public int cbStruct;
39+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
40+
public string wszCatalogFile;
41+
}
42+
43+
[DllImport("wintrust.dll", SetLastError = true)]
44+
public static extern bool CryptCATAdminAcquireContext(out IntPtr phCatAdmin, IntPtr pgSubsystem, uint dwFlags);
45+
46+
[DllImport("wintrust.dll", SetLastError = true)]
47+
public static extern bool CryptCATAdminCalcHashFromFileHandle(IntPtr hFile, ref uint pcbHash, byte[] pbHash, uint dwFlags);
48+
49+
[DllImport("wintrust.dll", SetLastError = true)]
50+
public static extern IntPtr CryptCATAdminEnumCatalogFromHash(IntPtr hCatAdmin, byte[] pbHash, uint cbHash, uint dwFlags, IntPtr phPrevCatInfo);
51+
52+
[DllImport("wintrust.dll", ExactSpelling = true, SetLastError = true)]
53+
[return: MarshalAs(UnmanagedType.Bool)]
54+
public static extern bool CryptCATCatalogInfoFromContext(
55+
IntPtr hCatInfo,
56+
ref CATALOG_INFO psCatInfo,
57+
uint dwFlags = 0u);
58+
59+
[DllImport("wintrust.dll", SetLastError = true)]
60+
public static extern bool CryptCATAdminReleaseCatalogContext(IntPtr hCatAdmin, IntPtr hCatInfo, uint dwFlags);
61+
62+
[DllImport("wintrust.dll", SetLastError = true)]
63+
public static extern bool CryptCATAdminReleaseContext(IntPtr hCatAdmin, uint dwFlags);
64+
65+
public const uint ERROR_INSUFFICIENT_BUFFER = 122;
66+
67+
static void Main(string[] args)
68+
{
69+
if (args.Length < 1)
70+
{
71+
Console.WriteLine("Usage: DumpCertificateInfo <filename>");
72+
return;
73+
}
74+
75+
if (!GetFileCertificateInfo(args[0]))
76+
{
77+
Console.WriteLine("Failed to get certificate information for file: " + args[0]);
78+
}
79+
}
80+
81+
static bool GetFileCertificateInfo(string filePath)
82+
{
83+
SafeFileHandle fileToCheck = null;
84+
try
85+
{
86+
fileToCheck = File.OpenRead(filePath)?.SafeFileHandle;
87+
88+
if (null == fileToCheck)
89+
{
90+
throw new Exception("Unable to get handle for file.");
91+
}
92+
}
93+
catch (Exception ex)
94+
{
95+
Console.WriteLine($"Failed to open file. Error: {ex.Message}");
96+
}
97+
98+
// Acquire a handle to the catalog administrator context
99+
// https://learn.microsoft.com/en-us/windows/win32/api/mscat/nf-mscat-cryptcatadminacquirecontext
100+
IntPtr hCatAdmin = IntPtr.Zero;
101+
if (!CryptCATAdminAcquireContext(out hCatAdmin, IntPtr.Zero, 0))
102+
{
103+
Console.WriteLine($"CryptCATAdminAcquireContext failed. Error: {Marshal.GetLastWin32Error()}");
104+
return false;
105+
}
106+
107+
// First, call CryptCATAdminCalcHashFromFileHandle to get the hash size
108+
// https://learn.microsoft.com/en-us/windows/win32/api/mscat/nf-mscat-cryptcatadmincalchashfromfilehandle
109+
uint hashSize = 0;
110+
CryptCATAdminCalcHashFromFileHandle(fileToCheck.DangerousGetHandle(), ref hashSize, null, 0);
111+
if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER)
112+
{
113+
Console.WriteLine($"CryptCATAdminCalcHashFromFileHandle failed to get the buffer size. Error: {Marshal.GetLastWin32Error()}");
114+
CryptCATAdminReleaseContext(hCatAdmin, 0);
115+
fileToCheck.Dispose();
116+
return false;
117+
}
118+
119+
// Allocate buffer for the hash using the hash size from the previous call to CryptCATAdminCalcHashFromFileHandle
120+
var hash = new byte[hashSize];
121+
if (!CryptCATAdminCalcHashFromFileHandle(fileToCheck.DangerousGetHandle(), ref hashSize, hash, 0))
122+
{
123+
Console.WriteLine($"CryptCATAdminCalcHashFromFileHandle failed. Error: {Marshal.GetLastWin32Error()}");
124+
fileToCheck.Dispose();
125+
return false;
126+
}
127+
128+
fileToCheck.Dispose();
129+
130+
// Display the calculated hash
131+
Console.WriteLine($"PESHA1 File Hash: {BitConverter.ToString(hash).Replace("-", string.Empty)}");
132+
133+
// Enumerate the catalog files that contain the specified hash
134+
// https://learn.microsoft.com/en-us/windows/win32/api/mscat/nf-mscat-cryptcatadminenumcatalogfromhash
135+
IntPtr hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, hash, hashSize, 0, IntPtr.Zero);
136+
if (hCatInfo == IntPtr.Zero)
137+
{
138+
Console.WriteLine($"CryptCATAdminEnumCatalogFromHash failed. Error: {Marshal.GetLastWin32Error()}");
139+
CryptCATAdminReleaseContext(hCatAdmin, 0);
140+
return false;
141+
}
142+
143+
// Get the catalog information from the context (namely the catalog file path)
144+
// https://learn.microsoft.com/en-us/windows/win32/api/mscat/nf-mscat-cryptcatcataloginfofromcontext
145+
CATALOG_INFO catInfo = new CATALOG_INFO
146+
{
147+
cbStruct = Marshal.SizeOf(typeof(CATALOG_INFO))
148+
};
149+
if (!CryptCATCatalogInfoFromContext(hCatInfo, ref catInfo))
150+
{
151+
Console.WriteLine($"CryptCATCatalogInfoFromContext failed. Error: {Marshal.GetLastWin32Error()}");
152+
CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);
153+
CryptCATAdminReleaseContext(hCatAdmin, 0);
154+
return false;
155+
}
156+
157+
// Display the catalog file path
158+
Console.WriteLine($"Catalog File Path: {catInfo.wszCatalogFile}");
159+
160+
// Verify and display the signature from the catalog file
161+
// Counter-signatures are not supported in this example
162+
bool result = VerifyCertificate(catInfo.wszCatalogFile);
163+
164+
CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);
165+
CryptCATAdminReleaseContext(hCatAdmin, 0);
166+
167+
return result;
168+
}
169+
170+
// Verify the certificate from the catalog file and display the certificate details
171+
static bool VerifyCertificate(string catFilePath)
172+
{
173+
X509Certificate cert = X509Certificate.CreateFromCertFile(catFilePath);
174+
175+
if (cert == null)
176+
{
177+
Console.WriteLine("Failed to load certificate from catalog file.");
178+
return false;
179+
}
180+
181+
// Display certificate details
182+
DisplayCertificateInfo(cert);
183+
184+
// Do the validation
185+
X509Chain chain = new X509Chain();
186+
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
187+
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
188+
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
189+
chain.ChainPolicy.VerificationTime = DateTime.Now;
190+
191+
bool isValid = chain.Build(new X509Certificate2(cert));
192+
193+
foreach (X509ChainStatus status in chain.ChainStatus)
194+
{
195+
if (status.Status != X509ChainStatusFlags.NoError)
196+
{
197+
Console.WriteLine($"Chain error: {status.StatusInformation}");
198+
isValid = false;
199+
}
200+
}
201+
202+
if (isValid)
203+
Console.WriteLine("Certificate chain verified successfully.");
204+
205+
return isValid;
206+
}
207+
208+
static void DisplayCertificateInfo(X509Certificate cert)
209+
{
210+
X509Certificate2 cert2 = new X509Certificate2(cert);
211+
Console.WriteLine($"Subject: {cert2.Subject}");
212+
Console.WriteLine($"Issuer: {cert2.Issuer}");
213+
Console.WriteLine($"Serial Number: {cert2.SerialNumber}");
214+
Console.WriteLine($"Thumbprint: {cert2.Thumbprint}");
215+
Console.WriteLine($"Valid From: {cert2.NotBefore}");
216+
Console.WriteLine($"Valid To: {cert2.NotAfter}");
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)