Repository: dotnet/arcade
Pull request #15583: Enable Authenticode verification with SignCheck on .NET Core
Author: login: ellahathaway
Created: 2025-02-28T23:23:31Z | Merged: 2025-03-06T17:04:49Z

Closes https://github.com/dotnet/arcade/issues/15571


Diff:
diff --git a/src/SignCheck/Microsoft.SignCheck/Interop/WinCrypt.cs b/src/SignCheck/Microsoft.SignCheck/Interop/WinCrypt.cs
index 92a6ebbf8eb..2b274b30ff3 100644
--- a/src/SignCheck/Microsoft.SignCheck/Interop/WinCrypt.cs
+++ b/src/SignCheck/Microsoft.SignCheck/Interop/WinCrypt.cs
@@ -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";
diff --git a/src/SignCheck/Microsoft.SignCheck/Microsoft.DotNet.SignCheckLibrary.csproj b/src/SignCheck/Microsoft.SignCheck/Microsoft.DotNet.SignCheckLibrary.csproj
index 5af714fc818..c1306736f46 100644
--- a/src/SignCheck/Microsoft.SignCheck/Microsoft.DotNet.SignCheckLibrary.csproj
+++ b/src/SignCheck/Microsoft.SignCheck/Microsoft.DotNet.SignCheckLibrary.csproj
@@ -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" />
diff --git a/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs b/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs
index 3c0014bfa9e..6a437653615 100644
--- a/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs
+++ b/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs
@@ -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;
@@ -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()
             {
@@ -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;
@@ -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
@@ -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;
+        }
     }
 }
diff --git a/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCodeVerifier.cs b/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCodeVerifier.cs
index 73dc0297fa8..8a0a3fcf436 100644
--- a/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCodeVerifier.cs
+++ b/src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCodeVerifier.cs
@@ -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)
             {
diff --git a/src/SignCheck/Microsoft.SignCheck/Verification/ExeVerifier.cs b/src/SignCheck/Microsoft.SignCheck/Verification/ExeVerifier.cs
index 6444c37e96e..8059917efac 100644
--- a/src/SignCheck/Microsoft.SignCheck/Verification/ExeVerifier.cs
+++ b/src/SignCheck/Microsoft.SignCheck/Verification/ExeVerifier.cs
@@ -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
 {
@@ -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");
@@ -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.
@@ -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
     }
 }
diff --git a/src/SignCheck/Microsoft.SignCheck/Verification/PortableExecutableVerifier.cs b/src/SignCheck/Microsoft.SignCheck/Verification/PortableExecutableVerifier.cs
index 8803441f108..1064b435b0a 100644
--- a/src/SignCheck/Microsoft.SignCheck/Verification/PortableExecutableVerifier.cs
+++ b/src/SignCheck/Microsoft.SignCheck/Verification/PortableExecutableVerifier.cs
@@ -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;
@@ -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
                 {
diff --git a/src/SignCheck/Microsoft.SignCheck/Verification/SignatureVerificationManager.cs b/src/SignCheck/Microsoft.SignCheck/Verification/SignatureVerificationManager.cs
index 9563d292dad..1c9e6b9e862 100644
--- a/src/SignCheck/Microsoft.SignCheck/Verification/SignatureVerificationManager.cs
+++ b/src/SignCheck/Microsoft.SignCheck/Verification/SignatureVerificationManager.cs
@@ -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
@@ -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));
@@ -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));
         }


Reviews:
user: login: mmitche
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T00:27:46Z
user: login: mmitche
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T00:30:26Z
user: login: mmitche
body: Approved with minor comments/questions. You can add the netcore strong name signing call in a later PR if you want.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-06T00:31:35Z
user: login: ellahathaway
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T00:32:21Z
user: login: ellahathaway
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T00:35:28Z
user: login: mmitche
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T15:37:23Z

Review comments:
diff_hunk: @@ -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);
path: src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs
user: login: mmitche
body: Interesting. I didn't realize this works on Non-Windows. Have you verified it there?
created_at: 2025-03-06T00:27:46Z
author_association: MEMBER
diff_hunk: @@ -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.");
path: src/SignCheck/Microsoft.SignCheck/Verification/PortableExecutableVerifier.cs
user: login: mmitche
body: This actually can work. I had to implement this for signtool:

https://github.com/dotnet/arcade/blob/main/src/Microsoft.DotNet.SignTool/src/StrongName.cs#L110-L191
created_at: 2025-03-06T00:30:26Z
author_association: MEMBER
diff_hunk: @@ -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.");
path: src/SignCheck/Microsoft.SignCheck/Verification/PortableExecutableVerifier.cs
user: login: ellahathaway
body: I'm going to do this as part of a separate PR: https://github.com/dotnet/source-build/issues/4884. I have the working version locally and plan to publish that PR tomorrow.
created_at: 2025-03-06T00:32:21Z
author_association: MEMBER
diff_hunk: @@ -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);
path: src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs
user: login: ellahathaway
body: I have verified this, yes. Signed files are being reported as signed in my local testing and pipeline testing. This is output from a run on Linux:

```
AuthentiCode="Timestamp: 05/15/18 20:21:16 (sha1RSA), Timestamp: 05/15/18 20:21:16 (sha256RSA), AuthentiCode signed: True"
```
created_at: 2025-03-06T00:35:27Z
author_association: MEMBER
diff_hunk: @@ -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);
path: src/SignCheck/Microsoft.SignCheck/Verification/AuthentiCode.cs
user: login: mmitche
body: Awesome
created_at: 2025-03-06T15:37:23Z
author_association: MEMBER

PR comments:
user: login: ellahathaway
created_at: 2025-03-03T19:41:57Z
body: Doing verification with pipelines, then will publish
author_association: MEMBER
user: login: ellahathaway
created_at: 2025-03-03T22:32:44Z
body: https://dev.azure.com/dnceng/internal/_build/results?buildId=2654765&view=results
author_association: MEMBER