Skip to content

Commit de1d7a2

Browse files
authored
Merge pull request #7323 from sujitnayak/main
Provide option to fail ClickOnce publish if RFC3161 timestamping fails.
2 parents 288ea72 + 9916d03 commit de1d7a2

7 files changed

+80
-32
lines changed

src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd

+3
Original file line numberDiff line numberDiff line change
@@ -3386,6 +3386,9 @@ elementFormDefault="qualified">
33863386
<xs:attribute name="CertificateThumbprint" use="required" />
33873387
<xs:attribute name="SigningTarget" use="required" />
33883388
<xs:attribute name="TimestampUrl" />
3389+
<xs:attribute name="TargetFrameworkIdentifier" />
3390+
<xs:attribute name="TargetFrameworkVersion" />
3391+
<xs:attribute name="DisallowMansignTimestampFallback" />
33893392
</xs:extension>
33903393
</xs:complexContent>
33913394
</xs:complexType>

src/Tasks/ManifestUtil/SecurityUtil.cs

+30-6
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,25 @@ public static void SignFile(string certThumbprint,
528528
string path,
529529
string targetFrameworkVersion,
530530
string targetFrameworkIdentifier)
531+
{
532+
SignFile(certThumbprint, timestampUrl, path, targetFrameworkVersion, targetFrameworkIdentifier, false);
533+
}
534+
535+
/// <summary>
536+
/// Signs a ClickOnce manifest or PE file.
537+
/// </summary>
538+
/// <param name="certThumbprint">Hexadecimal string that contains the SHA-1 hash of the certificate.</param>
539+
/// <param name="timestampUrl">URL that specifies an address of a time stamping server.</param>
540+
/// <param name="path">Path of the file to sign with the certificate.</param>
541+
/// <param name="targetFrameworkVersion">Version of the .NET Framework for the target.</param>
542+
/// <param name="targetFrameworkIdentifier">.NET Framework identifier for the target.</param>
543+
/// <param name="disallowMansignTimestampFallback">Disallow fallback to legacy timestamping when RFC3161 timestamping fails during manifest signing</param>
544+
public static void SignFile(string certThumbprint,
545+
Uri timestampUrl,
546+
string path,
547+
string targetFrameworkVersion,
548+
string targetFrameworkIdentifier,
549+
bool disallowMansignTimestampFallback)
531550
{
532551
System.Resources.ResourceManager resources = new System.Resources.ResourceManager("Microsoft.Build.Tasks.Core.Strings.ManifestUtilities", typeof(SecurityUtilities).Module.Assembly);
533552

@@ -563,7 +582,7 @@ public static void SignFile(string certThumbprint,
563582
// Use SHA-256 digest for .NET Core apps
564583
isTargetFrameworkSha256Supported = true;
565584
}
566-
SignFileInternal(cert, timestampUrl, path, isTargetFrameworkSha256Supported, resources);
585+
SignFileInternal(cert, timestampUrl, path, isTargetFrameworkSha256Supported, resources, disallowMansignTimestampFallback);
567586
}
568587
else
569588
{
@@ -611,7 +630,12 @@ public static void SignFile(X509Certificate2 cert, Uri timestampUrl, string path
611630
SignFileInternal(cert, timestampUrl, path, true, resources);
612631
}
613632

614-
private static void SignFileInternal(X509Certificate2 cert, Uri timestampUrl, string path, bool targetFrameworkSupportsSha256, System.Resources.ResourceManager resources)
633+
private static void SignFileInternal(X509Certificate2 cert,
634+
Uri timestampUrl,
635+
string path,
636+
bool targetFrameworkSupportsSha256,
637+
System.Resources.ResourceManager resources,
638+
bool disallowMansignTimestampFallback = false)
615639
{
616640
if (cert == null)
617641
{
@@ -686,7 +710,7 @@ private static void SignFileInternal(X509Certificate2 cert, Uri timestampUrl, st
686710
if (timestampUrl == null)
687711
manifest.Sign(signer);
688712
else
689-
manifest.Sign(signer, timestampUrl.ToString());
713+
manifest.Sign(signer, timestampUrl.ToString(), disallowMansignTimestampFallback);
690714
doc.Save(path);
691715
}
692716
catch (Exception ex)
@@ -790,9 +814,9 @@ internal static string GetCommandLineParameters(string certThumbprint, Uri times
790814
if (timestampUrl != null)
791815
{
792816
commandLine.AppendFormat(CultureInfo.InvariantCulture,
793-
"{0} {1} ",
794-
useRFC3161Timestamp ? "/tr" : "/t",
795-
timestampUrl.ToString());
817+
"{0} {1} ",
818+
useRFC3161Timestamp ? "/tr" : "/t",
819+
timestampUrl.ToString());
796820
}
797821
commandLine.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\"", path);
798822
return commandLine.ToString();

src/Tasks/ManifestUtil/mansign2.cs

+30-23
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ internal void Sign(CmiManifestSigner2 signer)
319319
Sign(signer, null);
320320
}
321321

322-
internal void Sign(CmiManifestSigner2 signer, string timeStampUrl)
322+
internal void Sign(CmiManifestSigner2 signer, string timeStampUrl, bool disallowMansignTimestampFallback = false)
323323
{
324324
// Reset signer infos.
325325
_strongNameSignerInfo = null;
@@ -350,7 +350,7 @@ internal void Sign(CmiManifestSigner2 signer, string timeStampUrl)
350350

351351
// Now create the license DOM, and then sign it.
352352
licenseDom = CreateLicenseDom(signer, ExtractPrincipalFromManifest(), ComputeHashFromManifest(_manifestDom, _useSha256));
353-
AuthenticodeSignLicenseDom(licenseDom, signer, timeStampUrl, _useSha256);
353+
AuthenticodeSignLicenseDom(licenseDom, signer, timeStampUrl, _useSha256, disallowMansignTimestampFallback);
354354
}
355355
StrongNameSignManifestDom(_manifestDom, licenseDom, signer, _useSha256);
356356
}
@@ -676,7 +676,7 @@ private static XmlDocument CreateLicenseDom(CmiManifestSigner2 signer, XmlElemen
676676
return licenseDom;
677677
}
678678

679-
private static void AuthenticodeSignLicenseDom(XmlDocument licenseDom, CmiManifestSigner2 signer, string timeStampUrl, bool useSha256)
679+
private static void AuthenticodeSignLicenseDom(XmlDocument licenseDom, CmiManifestSigner2 signer, string timeStampUrl, bool useSha256, bool disallowMansignTimestampFallback)
680680
{
681681
// Make sure it is RSA, as this is the only one Fusion will support.
682682
#if RUNTIME_TYPE_NETCORE
@@ -747,7 +747,7 @@ private static void AuthenticodeSignLicenseDom(XmlDocument licenseDom, CmiManife
747747
// Time stamp it if requested.
748748
if (!string.IsNullOrEmpty(timeStampUrl))
749749
{
750-
TimestampSignedLicenseDom(licenseDom, timeStampUrl, useSha256);
750+
TimestampSignedLicenseDom(licenseDom, timeStampUrl, useSha256, disallowMansignTimestampFallback);
751751
}
752752

753753
// Wrap it inside a RelData element.
@@ -831,7 +831,7 @@ private static string ObtainRFC3161Timestamp(string timeStampUrl, string signatu
831831
return timestamp;
832832
}
833833

834-
private static void TimestampSignedLicenseDom(XmlDocument licenseDom, string timeStampUrl, bool useSha256)
834+
private static void TimestampSignedLicenseDom(XmlDocument licenseDom, string timeStampUrl, bool useSha256, bool disallowMansignTimestampFallback)
835835
{
836836
XmlNamespaceManager nsm = new XmlNamespaceManager(licenseDom.NameTable);
837837
nsm.AddNamespace("r", LicenseNamespaceUri);
@@ -850,31 +850,38 @@ private static void TimestampSignedLicenseDom(XmlDocument licenseDom, string tim
850850
// Catch CryptographicException to ensure fallback to old code (non-RFC3161)
851851
catch (CryptographicException)
852852
{
853-
Win32.CRYPT_DATA_BLOB timestampBlob = new Win32.CRYPT_DATA_BLOB();
853+
if (disallowMansignTimestampFallback)
854+
{
855+
throw;
856+
}
857+
else
858+
{
859+
Win32.CRYPT_DATA_BLOB timestampBlob = new Win32.CRYPT_DATA_BLOB();
854860

855-
byte[] licenseXml = Encoding.UTF8.GetBytes(licenseDom.OuterXml);
861+
byte[] licenseXml = Encoding.UTF8.GetBytes(licenseDom.OuterXml);
856862

857-
unsafe
858-
{
859-
fixed (byte* pbLicense = licenseXml)
863+
unsafe
860864
{
861-
Win32.CRYPT_DATA_BLOB licenseBlob = new Win32.CRYPT_DATA_BLOB();
862-
IntPtr pvLicense = new IntPtr(pbLicense);
863-
licenseBlob.cbData = (uint)licenseXml.Length;
864-
licenseBlob.pbData = pvLicense;
865-
866-
int hr = Win32.CertTimestampAuthenticodeLicense(ref licenseBlob, timeStampUrl, ref timestampBlob);
867-
if (hr != Win32.S_OK)
865+
fixed (byte* pbLicense = licenseXml)
868866
{
869-
throw new CryptographicException(hr);
867+
Win32.CRYPT_DATA_BLOB licenseBlob = new Win32.CRYPT_DATA_BLOB();
868+
IntPtr pvLicense = new IntPtr(pbLicense);
869+
licenseBlob.cbData = (uint)licenseXml.Length;
870+
licenseBlob.pbData = pvLicense;
871+
872+
int hr = Win32.CertTimestampAuthenticodeLicense(ref licenseBlob, timeStampUrl, ref timestampBlob);
873+
if (hr != Win32.S_OK)
874+
{
875+
throw new CryptographicException(hr);
876+
}
870877
}
871878
}
872-
}
873879

874-
byte[] timestampSignature = new byte[timestampBlob.cbData];
875-
Marshal.Copy(timestampBlob.pbData, timestampSignature, 0, timestampSignature.Length);
876-
Win32.HeapFree(Win32.GetProcessHeap(), 0, timestampBlob.pbData);
877-
timestamp = Encoding.UTF8.GetString(timestampSignature);
880+
byte[] timestampSignature = new byte[timestampBlob.cbData];
881+
Marshal.Copy(timestampBlob.pbData, timestampSignature, 0, timestampSignature.Length);
882+
Win32.HeapFree(Win32.GetProcessHeap(), 0, timestampBlob.pbData);
883+
timestamp = Encoding.UTF8.GetString(timestampSignature);
884+
}
878885
}
879886

880887
XmlElement asTimestamp = licenseDom.CreateElement("as", "Timestamp", AuthenticodeNamespaceUri);

src/Tasks/Microsoft.Common.CurrentVersion.targets

+2
Original file line numberDiff line numberDiff line change
@@ -5984,6 +5984,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
59845984
SigningTarget="$(_DeploymentApplicationDir)$(_DeploymentTargetApplicationManifestFileName)"
59855985
TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)"
59865986
TargetFrameworkVersion="$(TargetFrameworkVersion)"
5987+
DisallowMansignTimestampFallback="$(DisallowMansignTimestampFallback)"
59875988
Condition="'$(_DeploymentSignClickOnceManifests)'=='true'" />
59885989

59895990
<!-- Update entry point path in deploy manifest -->
@@ -6004,6 +6005,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
60046005
SigningTarget="$(PublishDir)$(TargetDeployManifestFileName)"
60056006
TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)"
60066007
TargetFrameworkVersion="$(TargetFrameworkVersion)"
6008+
DisallowMansignTimestampFallback="$(DisallowMansignTimestampFallback)"
60076009
Condition="'$(_DeploymentSignClickOnceManifests)'=='true'" />
60086010

60096011
<SignFile
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Microsoft.Build.Tasks.SignFile.DisallowMansignTimestampFallback.get -> bool
2+
Microsoft.Build.Tasks.SignFile.DisallowMansignTimestampFallback.set -> void
3+
static Microsoft.Build.Tasks.Deployment.ManifestUtilities.SecurityUtilities.SignFile(string certThumbprint, System.Uri timestampUrl, string path, string targetFrameworkVersion, string targetFrameworkIdentifier, bool disallowMansignTimestampFallback) -> void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Microsoft.Build.Tasks.SignFile.DisallowMansignTimestampFallback.get -> bool
2+
Microsoft.Build.Tasks.SignFile.DisallowMansignTimestampFallback.set -> void
3+
static Microsoft.Build.Tasks.Deployment.ManifestUtilities.SecurityUtilities.SignFile(string certThumbprint, System.Uri timestampUrl, string path, string targetFrameworkVersion, string targetFrameworkIdentifier, bool disallowMansignTimestampFallback) -> void

src/Tasks/SignFile.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,19 @@ public SignFile()
3838

3939
public string TimestampUrl { get; set; }
4040

41+
public bool DisallowMansignTimestampFallback { get; set; } = false;
42+
4143
public override bool Execute()
4244
{
4345
try
4446
{
45-
SecurityUtilities.SignFile(CertificateThumbprint,
46-
TimestampUrl == null ? null : new Uri(TimestampUrl),
47-
SigningTarget.ItemSpec, TargetFrameworkVersion, TargetFrameworkIdentifier);
47+
SecurityUtilities.SignFile(
48+
CertificateThumbprint,
49+
TimestampUrl == null ? null : new Uri(TimestampUrl),
50+
SigningTarget.ItemSpec,
51+
TargetFrameworkVersion,
52+
TargetFrameworkIdentifier,
53+
DisallowMansignTimestampFallback);
4854
return true;
4955
}
5056
catch (ArgumentException ex) when (ex.ParamName.Equals("certThumbprint"))

0 commit comments

Comments
 (0)