From 52d4afb140c03b186418cf1b6c8fa6ee6b29e3c4 Mon Sep 17 00:00:00 2001 From: Alexey Mamontov Date: Sun, 7 Jan 2024 16:54:13 +0200 Subject: [PATCH 1/3] Make SigAppend ignored for ps1 --- .../AuthenticodeKeyVaultSigner.cs | 22 +++-- .../AuthenticodeKeyVaultSignerTests.cs | 85 +++++++++++++++++++ 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/src/AzureSign.Core/AuthenticodeKeyVaultSigner.cs b/src/AzureSign.Core/AuthenticodeKeyVaultSigner.cs index 0a2b0a5..bb94f85 100644 --- a/src/AzureSign.Core/AuthenticodeKeyVaultSigner.cs +++ b/src/AzureSign.Core/AuthenticodeKeyVaultSigner.cs @@ -101,17 +101,25 @@ static char[] NullTerminate(ReadOnlySpan str) if (appendSignature) { - if (Environment.OSVersion.Version < _win11Version) + if (!path.EndsWith(".dll".AsSpan(), StringComparison.InvariantCultureIgnoreCase) && + !path.EndsWith(".exe".AsSpan(), StringComparison.InvariantCultureIgnoreCase)) { - // SignerSignEx3 silently succeeds with append on Windows 10 but does not actually append, so throw an error if we are not on Windows 11 or later. - throw new PlatformNotSupportedException("Appending signatures requires Windows 11 or later."); + logger?.LogWarning("SIG_APPEND is not supported for this file extention and will be ignored."); } - if (_timeStampConfiguration.Type == TimeStampType.Authenticode) + else { - // E_INVALIDARG is expected from SignerSignEx3, no need to override this error, log warning for troubleshooting - logger?.LogWarning("If you set the dwTimestampFlags parameter to SIGNER_TIMESTAMP_AUTHENTICODE, you cannot set the dwFlags parameter to SIG_APPEND."); + if (Environment.OSVersion.Version < _win11Version) + { + // SignerSignEx3 silently succeeds with append on Windows 10 but does not actually append, so throw an error if we are not on Windows 11 or later. + throw new PlatformNotSupportedException("Appending signatures requires Windows 11 or later."); + } + if (_timeStampConfiguration.Type == TimeStampType.Authenticode) + { + // E_INVALIDARG is expected from SignerSignEx3, no need to override this error, log warning for troubleshooting + logger?.LogWarning("If you set the dwTimestampFlags parameter to SIGNER_TIMESTAMP_AUTHENTICODE, you cannot set the dwFlags parameter to SIG_APPEND."); + } + flags |= SignerSignEx3Flags.SIG_APPEND; } - flags |= SignerSignEx3Flags.SIG_APPEND; } SignerSignTimeStampFlags timeStampFlags; diff --git a/test/AzureSign.Core.Tests/AuthenticodeKeyVaultSignerTests.cs b/test/AzureSign.Core.Tests/AuthenticodeKeyVaultSignerTests.cs index df5bbb4..aa5d7cf 100644 --- a/test/AzureSign.Core.Tests/AuthenticodeKeyVaultSignerTests.cs +++ b/test/AzureSign.Core.Tests/AuthenticodeKeyVaultSignerTests.cs @@ -112,6 +112,84 @@ public void ShouldSignExeWithRSASigningCertificates_Sha256FileDigest_WithTimesta } } + [Theory] + [MemberData(nameof(RsaCertificates))] + public void ShouldSignPS1WithRSASigningCertificates_Sha1FileDigest(string certificate) + { + var signingCert = new X509Certificate2(certificate, "test", X509KeyStorageFlags.EphemeralKeySet); + var signer = new AuthenticodeKeyVaultSigner(signingCert.GetRSAPrivateKey(), signingCert, HashAlgorithmName.SHA1, TimeStampConfiguration.None); + var fileToSign = GetPS1FileToSign(); + var result = signer.SignFile(fileToSign, null, null, null); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: true); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: false); + Assert.Equal(0, result); + } + + [Theory] + [MemberData(nameof(RsaCertificates))] + public void ShouldSignPS1WithRSASigningCertificates_Sha256FileDigest(string certificate) + { + var signingCert = new X509Certificate2(certificate, "test", X509KeyStorageFlags.EphemeralKeySet); + var signer = new AuthenticodeKeyVaultSigner(signingCert.GetRSAPrivateKey(), signingCert, HashAlgorithmName.SHA256, TimeStampConfiguration.None); + var fileToSign = GetPS1FileToSign(); + var result = signer.SignFile(fileToSign, null, null, null); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: true); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: false); + Assert.Equal(0, result); + } + + + [Theory] + [MemberData(nameof(ECDsaCertificates))] + public void ShouldSignPS1WithECDsaSigningCertificates_Sha256FileDigest(string certificate) + { + var signingCert = new X509Certificate2(certificate, "test", X509KeyStorageFlags.EphemeralKeySet); + var signer = new AuthenticodeKeyVaultSigner(signingCert.GetECDsaPrivateKey(), signingCert, HashAlgorithmName.SHA256, TimeStampConfiguration.None); + var fileToSign = GetPS1FileToSign(); + var result = signer.SignFile(fileToSign, null, null, null); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: true); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: false); + Assert.Equal(0, result); + } + + [Theory] + [MemberData(nameof(ECDsaCertificates))] + public void ShouldSignPS1WithECDsaSigningCertificates_Sha256FileDigest_WithTimestamps(string certificate) + { + var signingCert = new X509Certificate2(certificate, "test", X509KeyStorageFlags.EphemeralKeySet); + var timestampConfig = new TimeStampConfiguration("http://timestamp.digicert.com", HashAlgorithmName.SHA256, TimeStampType.RFC3161); + var signer = new AuthenticodeKeyVaultSigner(signingCert.GetECDsaPrivateKey(), signingCert, HashAlgorithmName.SHA256, timestampConfig); + var fileToSign = GetPS1FileToSign(); + var result = signer.SignFile(fileToSign, null, null, null); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: true); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: false); + Assert.Equal(0, result); + } + + + [Theory] + [MemberData(nameof(RsaCertificates))] + public void ShouldSignPS1WithRSASigningCertificates_Sha256FileDigest_WithTimestamps(string certificate) + { + var signingCert = new X509Certificate2(certificate, "test", X509KeyStorageFlags.EphemeralKeySet); + var timestampConfig = new TimeStampConfiguration("http://timestamp.digicert.com", HashAlgorithmName.SHA256, TimeStampType.RFC3161); + var signer = new AuthenticodeKeyVaultSigner(signingCert.GetRSAPrivateKey(), signingCert, HashAlgorithmName.SHA256, timestampConfig); + var fileToSign = GetPS1FileToSign(); + var result = signer.SignFile(fileToSign, null, null, null); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: true); + Assert.Equal(0, result); + result = signer.SignFile(fileToSign, null, null, null, appendSignature: false); + Assert.Equal(0, result); + } private string GetFileToSign() { var guid = Guid.NewGuid(); @@ -119,6 +197,13 @@ private string GetFileToSign() File.Copy("signtarget.exe", path); return path; } + private string GetPS1FileToSign() + { + var guid = Guid.NewGuid(); + var path = Path.Combine(_scratchDirectory.FullName, $"{guid}.ps1"); + File.WriteAllText(path, "Write-Host \"Hello, World!\""); + return path; + } public void Dispose() { From 53ec2ac93cacee8502f68e5eadd910c1b231688c Mon Sep 17 00:00:00 2001 From: varonisarchitect <109017231+varonisarchitect@users.noreply.github.com> Date: Wed, 20 Dec 2023 12:07:51 +0200 Subject: [PATCH 2/3] Update README.md Remove limitations and add --append-signature --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 938187a..006f15f 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,9 @@ The `--help` or `sign --help` option provides more detail about each parameter. * `--skip-signed` [short: `-s`, required: no]: If a file is already signed it will be skipped, rather than replacing the existing signature. +* `--append-signature` [short: `-as`, required: no]: If a file is already signed it will append the signature, just sign otherwise. It has + no effect when used with `--skip-signed` + ### Advanced * `--page-hashing` [short: `-ph`, required: no]: Causes the Authenticode signing process to generate hashes of pages for verifying when @@ -148,7 +151,3 @@ a status code according to the complete signing operations. ## Requirements Windows 10 or Windows Server 2016 is required. - -## Current Limitations - -Dual signing is not supported. This appears to be a limitation of the API used. From 59d326e3fa8be8fa5091e7d72883e8f10b0826bb Mon Sep 17 00:00:00 2001 From: Alexey Mamontov Date: Sun, 7 Jan 2024 18:19:20 +0200 Subject: [PATCH 3/3] Mention not '.exe' and not '.dll' files in Readme.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 006f15f..74dce58 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,8 @@ The `--help` or `sign --help` option provides more detail about each parameter. * `--skip-signed` [short: `-s`, required: no]: If a file is already signed it will be skipped, rather than replacing the existing signature. -* `--append-signature` [short: `-as`, required: no]: If a file is already signed it will append the signature, just sign otherwise. It has - no effect when used with `--skip-signed` +* `--append-signature` [short: `-as`, required: no]: If '.exe' or '.dll' file is already signed it will append the signature, just sign otherwise. + It has no effect when used with `--skip-signed` or not '.exe' and not '.dll' files. ### Advanced