Search code examples
powershellcertificatex509certificate2powershell-corepowershell-7.3

How to get the TBS (To be signed) value of a signed file in PowerShell?


Looking for a way to get the TBS of a file that has digital signature in PowerShell 7.3+

Tried a few things like:

add-type -AssemblyName system.security.cryptography.X509Certificates

$cert = new-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList <file path>

$RawdDta = $cert.RawData

But not sure what the next steps are.

Let me clarify

This is the TBS value

enter image description here

I want to get them in PowerShell. The image is the XML output of running commands from the built-in ConfigCI module.

Get-AuthenticodeSignature doesn't show that value.

The TBS value shown in the screenshot belongs to this certificate which is not installed in the local cert store

enter image description here

Update:

https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/event-tag-explanations

For well-known roots, the TBS hashes for the certificates are baked into the code for Windows Defender Application Control. For example, they don’t need to be listed as TBS hashes in the policy file.

This is saying the TBS value is hash.


Solution

  • Apparently the systems I used for extracting the tbsCertificate and/or hashing it let me down. The algorithm for this value is the hash of the tbsCertificate using whatever algorithm the CA used to sign the certificate. So it's the hash value that the CA computes when signing (and that a verifier computes when verifying the chain). This program demonstrates it:

    using System;
    using System.Formats.Asn1;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    
    namespace TbsHasher
    {
        internal static class TbsHasher
        {
            private static void Main(string[] args)
            {
                using (X509Certificate2 cert = new X509Certificate2(args[0]))
                {
                    AsnReader reader = new AsnReader(cert.RawData, AsnEncodingRules.DER);
                    AsnReader certificate = reader.ReadSequence();
                    ReadOnlyMemory<byte> tbsCertificate = certificate.ReadEncodedValue();
                    AsnReader signatureAlgorithm = certificate.ReadSequence();
                    string algorithmOid = signatureAlgorithm.ReadObjectIdentifier();
    
                    Span<byte> hash = stackalloc byte[512 >> 3];
                    int written = Hash(tbsCertificate.Span, algorithmOid, hash);
    
                    Console.WriteLine(Convert.ToHexString(hash.Slice(0, written)));
                }
            }
    
            private static int Hash(
                ReadOnlySpan<byte> source,
                string algorithmOid,
                Span<byte> destination)
            {
                const string DsaWithSha1 = "1.2.840.10040.4.3";
                const string DsaWithSha256 = "2.16.840.1.101.3.4.3.2";
                const string DsaWithSha384 = "2.16.840.1.101.3.4.3.3";
                const string DsaWithSha512 = "2.16.840.1.101.3.4.3.4";
                const string ECDsaWithSha1 = "1.2.840.10045.4.1";
                const string ECDsaWithSha256 = "1.2.840.10045.4.3.2";
                const string ECDsaWithSha384 = "1.2.840.10045.4.3.3";
                const string ECDsaWithSha512 = "1.2.840.10045.4.3.4";
                const string RsaPkcs1Md5 = "1.2.840.113549.1.1.4";
                const string RsaPkcs1Sha1 = "1.2.840.113549.1.1.5";
                const string RsaPkcs1Sha256 = "1.2.840.113549.1.1.11";
                const string RsaPkcs1Sha384 = "1.2.840.113549.1.1.12";
                const string RsaPkcs1Sha512 = "1.2.840.113549.1.1.13";
    
                switch (algorithmOid)
                {
                    case RsaPkcs1Md5:
                        return MD5.HashData(source, destination);
                    case DsaWithSha1:
                    case ECDsaWithSha1:
                    case RsaPkcs1Sha1:
                        return SHA1.HashData(source, destination);
                    case DsaWithSha256:
                    case ECDsaWithSha256:
                    case RsaPkcs1Sha256:
                        return SHA256.HashData(source, destination);
                    case DsaWithSha384:
                    case ECDsaWithSha384:
                    case RsaPkcs1Sha384:
                        return SHA384.HashData(source, destination);
                    case DsaWithSha512:
                    case ECDsaWithSha512:
                    case RsaPkcs1Sha512:
                        return SHA512.HashData(source, destination);
                }
    
                throw new ArgumentOutOfRangeException($"No handler for algorithm {algorithmOid}");
            }
        }
    }
    

    When run on the PCA 2010 certificate (as downloaded from crt.sh cert 12729283), it outputs your expected 121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195

    This is not a thing that is built into .NET, and it probably isn't a PowerShell intrinsic cmdlet/function for it, either. The System.Formats.Asn1 package used here should be callable from PowerShell, but you'll have to translate the C# to PS, of course.