Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SignCheck/Microsoft.SignCheck/Interop/WinCrypt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ public static class WinCrypt
public const int CMSG_SIGNER_CERT_ID_PARAM = 38;
public const int CMSG_CMS_SIGNER_INFO_PARAM = 39;

public const string SPC_INDIRECT_DATA_OBJID = "1.3.6.1.4.1.311.2.1.4";
public const string szOID_RSA_signingTime = "1.2.840.113549.1.9.5";
public const string szOID_RSA_counterSign = "1.2.840.113549.1.9.6";
public const string szOID_RFC3161_counterSign = "1.3.6.1.4.1.311.3.3.1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,16 @@

<ItemGroup Condition="'$(TargetFramework)' == '$(NetToolCurrent)'">
<!-- Interop -->
<Compile Remove="Interop\**" />
<EmbeddedResource Remove="Interop\**" />
<Compile Remove="Interop\ICLRStrongName.cs" />
<Compile Remove="Interop\Ole32.cs" />
<Compile Remove="Interop\StructuredStorage.cs" />

<!-- Unsupported file types -->
<Compile Remove="Verification\AuthentiCode.cs" />
<Compile Remove="Verification\AuthentiCodeVerifier.cs" />
<Compile Remove="Verification\CabVerifier.cs" />
<Compile Remove="Verification\ExeVerifier.cs" />
<Compile Remove="Verification\JarVerifier.cs" />
<Compile Remove="Verification\MsiVerifier.cs" />
<Compile Remove="Verification\MspVerifier.cs" />
<Compile Remove="Verification\MsuVerifier.cs" />
<Compile Remove="Verification\PortableExecutableVerifier.cs" />
<Compile Remove="Verification\StrongName.cs" />
<Compile Remove="Verification\VsixVerifier.cs" />
<Compile Remove="Verification\Jar\JarFile.cs" />
Expand Down
187 changes: 145 additions & 42 deletions src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.SignCheck.Interop;
using System;
using System.Linq;
using System.Collections.Generic;
Expand All @@ -11,12 +10,23 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using Microsoft.SignCheck.Interop;
#if NET
using System.Reflection.PortableExecutable;
#endif

namespace Microsoft.SignCheck.Verification
{
public static class AuthentiCode
{
public static uint IsSigned(string path)
{
public static bool IsSigned(string path, SignatureVerificationResult svr) =>
string.IsNullOrEmpty(path) ? false : IsSignedInternal(path, svr);

public static IEnumerable<Timestamp> GetTimestamps(string path) =>
string.IsNullOrEmpty(path) ? Enumerable.Empty<Timestamp>() : GetTimestampsInternal(path);

#if NETFRAMEWORK
private static bool IsSignedInternal(string path, SignatureVerificationResult svr)
{
WinTrustFileInfo fileInfo = new WinTrustFileInfo()
{
Expand Down Expand Up @@ -50,55 +60,23 @@ public static uint IsSigned(string path)
Marshal.StructureToPtr(data, pData, true);
Marshal.StructureToPtr(WinTrust.WINTRUST_ACTION_GENERIC_VERIFY_V2, pGuid, true);

uint result = WinTrust.WinVerifyTrust(IntPtr.Zero, pGuid, pData);
uint hrresult = WinTrust.WinVerifyTrust(IntPtr.Zero, pGuid, pData);

Marshal.FreeHGlobal(pGuid);
Marshal.FreeHGlobal(pData);

return result;
}

/// <summary>
/// Searches the unsigned attributes in the counter signature for a timestamp token.
/// </summary>
/// <param name="unsignedAttribute"></param>
/// <returns></returns>
public static IEnumerable<Timestamp> GetTimestampsFromCounterSignature(AsnEncodedData unsignedAttribute)
{
var timestamps = new List<Timestamp>();
var rfc3161CounterSignature = new Pkcs9AttributeObject(unsignedAttribute);
SignedCms rfc3161Message = new SignedCms();
rfc3161Message.Decode(rfc3161CounterSignature.RawData);

foreach (SignerInfo rfc3161SignerInfo in rfc3161Message.SignerInfos)
// Log non-zero HRESULTs
if (hrresult != 0)
{
if (String.Equals(rfc3161Message.ContentInfo.ContentType.Value, WinCrypt.szOID_TIMESTAMP_TOKEN, StringComparison.OrdinalIgnoreCase))
{
var timestampToken = NuGet.Packaging.Signing.TstInfo.Read(rfc3161Message.ContentInfo.Content);

var timeStamp = new Timestamp
{
SignedOn = timestampToken.GenTime.LocalDateTime,
EffectiveDate = Convert.ToDateTime(rfc3161SignerInfo.Certificate.GetEffectiveDateString()).ToLocalTime(),
ExpiryDate = Convert.ToDateTime(rfc3161SignerInfo.Certificate.GetExpirationDateString()).ToLocalTime(),
SignatureAlgorithm = rfc3161SignerInfo.Certificate.SignatureAlgorithm.FriendlyName
};

timestamps.Add(timeStamp);
}
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
svr.AddDetail(DetailKeys.Error, String.Format(SignCheckResources.ErrorHResult, hrresult, errorMessage));
}

return timestamps;
return hrresult == 0;
}

public static IEnumerable<Timestamp> GetTimestamps(string path)
private static IEnumerable<Timestamp> GetTimestampsInternal(string path)
{
if (String.IsNullOrEmpty(path))
{
return null;
}

var timestamps = new List<Timestamp>();
int msgAndCertEncodingType;
int msgContentType;
int formatType;
Expand Down Expand Up @@ -141,6 +119,104 @@ public static IEnumerable<Timestamp> GetTimestamps(string path)
var signedCms = new SignedCms();
signedCms.Decode(vData);

return ExtractTimestamps(signedCms);
}
#else
private static bool IsSignedInternal(string path, SignatureVerificationResult svr)
{
try
{
SignedCms signedCms = ReadSecurityInfo(path);
if (signedCms == null)
{
return false;
}

if (signedCms.ContentInfo.ContentType.Value != WinCrypt.SPC_INDIRECT_DATA_OBJID)
{
throw new CryptographicException($"Invalid content type: {signedCms.ContentInfo.ContentType.Value}");
}

SignerInfoCollection signerInfos = signedCms.SignerInfos;
SignerInfo signerInfo = GetPrimarySignerInfo(signerInfos);

// Check the signatures
signerInfo.CheckSignature(signedCms.Certificates, true);
signedCms.CheckSignature(signedCms.Certificates, true);

return true;
}
catch (Exception ex)
{
svr.AddDetail(DetailKeys.Error, ex.Message);
return false;
}
}

private static IEnumerable<Timestamp> GetTimestampsInternal(string path)
{
SignedCms signedCms = ReadSecurityInfo(path);
if (signedCms == null)
{
return Enumerable.Empty<Timestamp>();
}

return ExtractTimestamps(signedCms);
}

private static SignedCms ReadSecurityInfo(string path)
{
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
using (PEReader peReader = new PEReader(fs))
{
var securityDirectory = peReader.PEHeaders.PEHeader.CertificateTableDirectory;
if (securityDirectory.RelativeVirtualAddress != 0 && securityDirectory.Size != 0)
{
int securityHeaderSize = 8; // 4(length of cert) + 2(cert revision) + 2(cert type)
if (securityDirectory.Size <= securityHeaderSize)
{
// No security entry - just the header
return null;
}

// Skip the header
fs.Position = securityDirectory.RelativeVirtualAddress + securityHeaderSize;
byte[] securityEntry = new byte[securityDirectory.Size - securityHeaderSize];

// Ensure the stream has enough data to read
if (fs.Length < fs.Position + securityEntry.Length)
{
throw new CryptographicException($"File '{path}' is too small to contain a valid security entry.");
}

// Read the security entry
fs.ReadExactly(securityEntry);

// Decode the security entry
var signedCms = new SignedCms();
signedCms.Decode(securityEntry);
return signedCms;
}
}

return null;
}

private static SignerInfo GetPrimarySignerInfo(SignerInfoCollection signerInfos)
{
int signerCount = signerInfos.Count;
if (signerCount != 1)
{
throw new CryptographicException($"Invalid number of signers: {signerCount}. Expected 1.");
}

return signerInfos[0];
}
#endif

private static IEnumerable<Timestamp> ExtractTimestamps(SignedCms signedCms)
{
var timestamps = new List<Timestamp>();
// Timestamp information can be stored in multiple sections.
// A single SHA1 stores the timestamp as a counter sign in the unsigned attributes
// Multiple authenticode signatures will store additional information as a nested signature
Expand Down Expand Up @@ -201,5 +277,32 @@ public static IEnumerable<Timestamp> GetTimestamps(string path)
return timestamps;
}

private static IEnumerable<Timestamp> GetTimestampsFromCounterSignature(AsnEncodedData unsignedAttribute)
{
var timestamps = new List<Timestamp>();
var rfc3161CounterSignature = new Pkcs9AttributeObject(unsignedAttribute);
SignedCms rfc3161Message = new SignedCms();
rfc3161Message.Decode(rfc3161CounterSignature.RawData);

foreach (SignerInfo rfc3161SignerInfo in rfc3161Message.SignerInfos)
{
if (String.Equals(rfc3161Message.ContentInfo.ContentType.Value, WinCrypt.szOID_TIMESTAMP_TOKEN, StringComparison.OrdinalIgnoreCase))
{
var timestampToken = NuGet.Packaging.Signing.TstInfo.Read(rfc3161Message.ContentInfo.Content);

var timeStamp = new Timestamp
{
SignedOn = timestampToken.GenTime.LocalDateTime,
EffectiveDate = Convert.ToDateTime(rfc3161SignerInfo.Certificate.GetEffectiveDateString()).ToLocalTime(),
ExpiryDate = Convert.ToDateTime(rfc3161SignerInfo.Certificate.GetExpirationDateString()).ToLocalTime(),
SignatureAlgorithm = rfc3161SignerInfo.Certificate.SignatureAlgorithm.FriendlyName
};

timestamps.Add(timeStamp);
}
}

return timestamps;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,9 @@ public override SignatureVerificationResult VerifySignature(string path, string
protected SignatureVerificationResult VerifyAuthentiCode(string path, string parent, string virtualPath)
{
var svr = new SignatureVerificationResult(path, parent, virtualPath);
uint hresult = AuthentiCode.IsSigned(path);
svr.IsAuthentiCodeSigned = hresult == 0;
svr.IsAuthentiCodeSigned = AuthentiCode.IsSigned(path, svr);
svr.IsSigned = svr.IsAuthentiCodeSigned;

// Log non-zero HRESULTs
if (hresult != 0)
{
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
svr.AddDetail(DetailKeys.Error, String.Format(SignCheckResources.ErrorHResult, hresult, errorMessage));
}

// TODO: Should only check if there is a signature, even if it's invalid
if (VerifyAuthenticodeTimestamps)
{
Expand Down
8 changes: 8 additions & 0 deletions src/SignCheck/Microsoft.SignCheck/Verification/ExeVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using System.IO;
using System.Linq;
using Microsoft.SignCheck.Logging;
#if NETFRAMEWORK
using Microsoft.Tools.WindowsInstallerXml;
#endif

namespace Microsoft.SignCheck.Verification
{
Expand All @@ -24,6 +26,7 @@ public override SignatureVerificationResult VerifySignature(string path, string

if (VerifyRecursive)
{
#if NETFRAMEWORK
if (PEHeader.ImageSectionHeaders.Select(s => s.SectionName).Contains(".wixburn"))
{
Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagSectionHeader, ".wixburn");
Expand Down Expand Up @@ -54,6 +57,9 @@ public override SignatureVerificationResult VerifySignature(string path, string
unbinder.DeleteTempFiles();
}
}
#else
Log.WriteMessage(LogVerbosity.Normal, $"Unable to verify contents of '{svr.FullPath}' on .NET Core.");
#endif
}

// TODO: Check for SFXCAB, IronMan, etc.
Expand All @@ -64,9 +70,11 @@ public override SignatureVerificationResult VerifySignature(string path, string
/// <summary>
/// Event handler for WiX Burn to extract a bundle.
/// </summary>
#if NETFRAMEWORK
private void UnbinderEventHandler(object sender, MessageEventArgs e)
{
Log.WriteMessage(LogVerbosity.Detailed, String.Format("{0}|{1}|{2}|{3}", e.Id, e.Level, e.ResourceName, e.SourceLineNumbers));
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public void VerifyStrongName(SignatureVerificationResult svr, PortableExecutable
// NGEN/CrossGen don't preserve StrongName signatures.
if (!svr.IsNativeImage)
{
#if NETFRAMEWORK
bool wasVerified = false;
int hresult = StrongName.ClrStrongName.StrongNameSignatureVerificationEx(svr.FullPath, fForceVerification: true, pfWasVerified: out wasVerified);
svr.IsStrongNameSigned = hresult == StrongName.S_OK;
Expand All @@ -73,6 +74,10 @@ public void VerifyStrongName(SignatureVerificationResult svr, PortableExecutable
svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailPublicKeyToken, publicToken);
}
}
#else
svr.IsStrongNameSigned = false;
svr.AddDetail(DetailKeys.StrongName, $"StrongName signature verification is not supported on .NET Core.");
#endif
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
using System.IO;
using System.IO.Compression;
using System.Linq;
# if NETFRAMEWORK
using Microsoft.SignCheck.Interop.PortableExecutable;
#endif
using Microsoft.SignCheck.Logging;

namespace Microsoft.SignCheck.Verification
Expand Down Expand Up @@ -89,18 +87,16 @@ public SignatureVerificationManager(Exclusions exclusions, Log log, SignatureVer
Options = options;

#if NETFRAMEWORK
AddFileVerifier(new CabVerifier(log, exclusions, options, ".cab"));
AddFileVerifier(new PortableExecutableVerifier(log, exclusions, options, ".dll"));
AddFileVerifier(new ExeVerifier(log, exclusions, options, ".exe"));
AddFileVerifier(new JarVerifier(log, exclusions, options));
AddFileVerifier(new AuthentiCodeVerifier(log, exclusions, options, ".js"));
AddFileVerifier(new MsiVerifier(log, exclusions, options));
AddFileVerifier(new MspVerifier(log, exclusions, options));
AddFileVerifier(new MsuVerifier(log, exclusions, options));
AddFileVerifier(new AuthentiCodeVerifier(log, exclusions, options, ".psd1"));
AddFileVerifier(new AuthentiCodeVerifier(log, exclusions, options, ".psm1"));
AddFileVerifier(new AuthentiCodeVerifier(log, exclusions, options, ".ps1"));
AddFileVerifier(new AuthentiCodeVerifier(log, exclusions, options, ".ps1xml"));
AddFileVerifier(new CabVerifier(log, exclusions, options, ".cab"));
AddFileVerifier(new JarVerifier(log, exclusions, options));
AddFileVerifier(new MsiVerifier(log, exclusions, options));
AddFileVerifier(new MspVerifier(log, exclusions, options));
AddFileVerifier(new MsuVerifier(log, exclusions, options));
AddFileVerifier(new VsixVerifier(log, exclusions, options));
#else
AddFileVerifier(new DebVerifier(log, exclusions, options));
Expand All @@ -111,8 +107,10 @@ public SignatureVerificationManager(Exclusions exclusions, Log log, SignatureVer
AddFileVerifier(new TarVerifier(log, exclusions, options, ".gz"));
AddFileVerifier(new RpmVerifier(log, exclusions, options));
#endif
AddFileVerifier(new ExeVerifier(log, exclusions, options, ".exe"));
AddFileVerifier(new LzmaVerifier(log, exclusions, options));
AddFileVerifier(new NupkgVerifier(log, exclusions, options));
AddFileVerifier(new PortableExecutableVerifier(log, exclusions, options, ".dll"));
AddFileVerifier(new XmlVerifier(log, exclusions, options));
AddFileVerifier(new ZipVerifier(log, exclusions, options));
}
Expand Down
Loading