Issue #28402: Adds signed catalog files for stdlib on Windows.
diff --git a/Misc/NEWS b/Misc/NEWS
index bb4f029..388a757 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -226,6 +226,8 @@
 Windows
 -------
 
+- Issue #28402: Adds signed catalog files for stdlib on Windows.
+
 - Issue #28333: Enables Unicode for ps1/ps2 and input() prompts. (Patch by
   Eryk Sun)
 
diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props
index 543b4ca..7012170 100644
--- a/PCbuild/pyproject.props
+++ b/PCbuild/pyproject.props
@@ -147,11 +147,13 @@
              Targets="CleanAll" />
   </Target>
 
-  <PropertyGroup Condition="'$(SigningCertificate)' != '' and $(SupportSigning)">
-    <SignToolPath Condition="'$(SignToolPath)' == '' or !Exists($(SignToolPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot81)\bin\x86\signtool.exe</SignToolPath>
-    <SignToolPath Condition="!Exists($(SignToolPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot)\bin\x86\signtool.exe</SignToolPath>
-    <SignToolPath Condition="!Exists($(SignToolPath))">$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A@InstallationFolder)\Bin\signtool.exe</SignToolPath>
-    <_SignCommand Condition="Exists($(SignToolPath))">"$(SignToolPath)" sign /q /n "$(SigningCertificate)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)"</_SignCommand>
+  <PropertyGroup>
+    <SdkBinPath Condition="'$(SdkBinPath)' == '' or !Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot10)\bin\x86</SdkBinPath>
+    <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot81)\bin\x86</SdkBinPath>
+    <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot)\bin\x86</SdkBinPath>
+    <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A@InstallationFolder)\Bin\</SdkBinPath>
+    <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /q /n "$(SigningCertificate)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)"</_SignCommand>
+    <_MakeCatCommand Condition="Exists($(SdkBinPath))">"$(SdkBinPath)\makecat.exe"</_MakeCatCommand>
   </PropertyGroup>
   
   <Target Name="_SignBuild" AfterTargets="AfterBuild" Condition="'$(SigningCertificate)' != '' and $(SupportSigning)">
diff --git a/Tools/msi/common.wxs b/Tools/msi/common.wxs
index 1949e81..398d94a 100644
--- a/Tools/msi/common.wxs
+++ b/Tools/msi/common.wxs
@@ -63,7 +63,9 @@
     <!-- Top-level directories -->
     <Fragment>
         <DirectoryRef Id="InstallDirectory">
-            <Directory Id="DLLs" Name="DLLs" />
+            <Directory Id="DLLs" Name="DLLs">
+                <Directory Id="Catalogs" />
+            </Directory>
         </DirectoryRef>
     </Fragment>
 
diff --git a/Tools/msi/lib/lib.wixproj b/Tools/msi/lib/lib.wixproj
index 64e5878..26311ea 100644
--- a/Tools/msi/lib/lib.wixproj
+++ b/Tools/msi/lib/lib.wixproj
@@ -27,6 +27,7 @@
             <TargetBase>$(PySourcePath)Lib</TargetBase>
             <Target_>Lib\</Target_>
             <Group>lib_py</Group>
+            <IncludeInCat>true</IncludeInCat>
         </InstallFiles>
     </ItemGroup>
     
diff --git a/Tools/msi/lib/lib.wxs b/Tools/msi/lib/lib.wxs
index 2b04bcb..2a3b9ec 100644
--- a/Tools/msi/lib/lib.wxs
+++ b/Tools/msi/lib/lib.wxs
@@ -11,6 +11,7 @@
             <ComponentGroupRef Id="lib_py" />
             <ComponentGroupRef Id="lib_files" />
             <ComponentGroupRef Id="lib_extensions" />
+            <ComponentGroupRef Id="lib_cat" />
             <ComponentRef Id="OptionalFeature" />
         </Feature>
     </Product>
diff --git a/Tools/msi/lib/lib_files.wxs b/Tools/msi/lib/lib_files.wxs
index 804ab01..eb26f8d 100644
--- a/Tools/msi/lib/lib_files.wxs
+++ b/Tools/msi/lib/lib_files.wxs
@@ -70,4 +70,11 @@
             </Component>
         </ComponentGroup>
     </Fragment>
+    <Fragment>
+        <ComponentGroup Id="lib_cat">
+            <Component Id="lib_cat" Directory="Catalogs" Guid="*">
+                <File Name="python_lib.cat" KeyPath="yes" />
+            </Component>
+        </ComponentGroup>
+    </Fragment>
 </Wix>
diff --git a/Tools/msi/msi.props b/Tools/msi/msi.props
index 745fc54..60abba1 100644
--- a/Tools/msi/msi.props
+++ b/Tools/msi/msi.props
@@ -11,6 +11,7 @@
         <Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
         <Platform Condition="'$(Platform)' == ''">x86</Platform>
         <InstallScope Condition="'$(InstallScope)' != 'perMachine'">perUser</InstallScope>
+        <_MakeCatCommand Condition="'$(_MakeCatCommand)' == ''">makecat</_MakeCatCommand>
     </PropertyGroup>
 
     <Import Project="wix.props" />
@@ -103,6 +104,7 @@
             <Group>generated_filelist</Group>
             <Condition></Condition>
             <DiskId></DiskId>
+            <IncludeInCat>false</IncludeInCat>
         </InstallFiles>
         <LinkerBindInputPaths>
             <Visible>false</Visible>
diff --git a/Tools/msi/msi.targets b/Tools/msi/msi.targets
index 86be35b..9283a1e 100644
--- a/Tools/msi/msi.targets
+++ b/Tools/msi/msi.targets
@@ -12,8 +12,10 @@
                 <_Source>%(Source)$([msbuild]::MakeRelative(%(SourceBase), %(FullPath)))</_Source>
                 <_Target>%(Target_)$([msbuild]::MakeRelative(%(TargetBase), %(FullPath)))</_Target>
             </InstallFiles>
+            
+            <_CatalogFiles Include="@(InstallFiles)" Condition="%(InstallFiles.IncludeInCat) and ''!=$([System.IO.File]::ReadAllText(%(InstallFiles.FullPath)))" />
         </ItemGroup>
-        
+
         <WriteLinesToFile File="$(_FileListTarget)" Lines="@(InstallFiles->'&quot;%(_Source)&quot;,&quot;%(_Target)&quot;,&quot;%(Group)&quot;,&quot;%(DiskId)&quot;,&quot;%(Condition)&quot;')" Overwrite="true" />
         <Exec Command='"$(PythonExe)" csv_to_wxs.py "$(_FileListTarget)" "$(_InstallFilesTarget)"'
               WorkingDirectory="$(MSBuildThisFileDirectory)" />
@@ -24,6 +26,35 @@
         </ItemGroup>
     </Target>
 
+    <Target Name="GenerateCatalog" AfterTargets="ProcessInstallFiles" Condition="'@(_CatalogFiles)' != ''">
+        <PropertyGroup>
+            <_CatFileSourceTarget>$(IntermediateOutputPath)$(MSBuildProjectName).cdf</_CatFileSourceTarget>
+            <_CatFileTarget>$(IntermediateOutputPath)python_$(MSBuildProjectName).cat</_CatFileTarget>
+            <_CatFile>[CatalogHeader]
+Name=$([System.IO.Path]::GetFileName($(_CatFileTarget)))
+ResultDir=$([System.IO.Path]::GetDirectoryName($(_CatFileTarget)))
+PublicVersion=1
+CatalogVersion=2
+HashAlgorithms=SHA256
+PageHashes=false
+EncodingType=
+
+[CatalogFiles]
+@(_CatalogFiles->'&lt;HASH&gt;%(Filename)%(Extension)=%(FullPath)','
+')
+</_CatFile>
+        </PropertyGroup>
+
+        <WriteLinesToFile File="$(_CatFileSourceTarget)" Lines="$(_CatFile)" Overwrite="true" />
+        <Exec Command='$(_MakeCatCommand) "$(_CatFileSourceTarget)"' WorkingDirectory="$(MSBuildThisFileDirectory)" />
+        <Exec Command='$(_SignCommand) "$(_CatFileTarget)"' WorkingDirectory="$(MSBuildThisFileDirectory)"
+              Condition="Exists($(_CatFileTarget)) and '$(_SignCommand)' != ''" />
+
+        <ItemGroup>
+            <FileWrites Include="$(_CatFileSourceTarget);$(_CatFileTarget)" />
+        </ItemGroup>
+    </Target>
+
     <Target Name="_TransformWxlTemplates" AfterTargets="PrepareForBuild" Inputs="@(WxlTemplate);$(PySourcePath)include\patchlevel.h" Outputs="$(IntermediateOutputPath)%(Filename).wxl">
         <PropertyGroup Condition="'@(WxlTemplate)' != ''">
             <_Content>$([System.IO.File]::ReadAllText(%(WxlTemplate.FullPath)).Replace(`{{ShortVersion}}`, `$(MajorVersionNumber).$(MinorVersionNumber)$(PyTestExt)`).Replace(`{{LongVersion}}`, `$(PythonVersion)$(PyTestExt)`).Replace(`{{Bitness}}`, `$(Bitness)`))</_Content>
diff --git a/Tools/msi/tools/tools.wixproj b/Tools/msi/tools/tools.wixproj
index f43cf33..b7fc41e 100644
--- a/Tools/msi/tools/tools.wixproj
+++ b/Tools/msi/tools/tools.wixproj
@@ -36,6 +36,7 @@
                 <TargetBase>$(PySourcePath)</TargetBase>
                 <Target_></Target_>
                 <Group>tools_py</Group>
+                <IncludeInCat>true</IncludeInCat>
         </InstallFiles>
     </ItemGroup>
     
diff --git a/Tools/msi/tools/tools.wxs b/Tools/msi/tools/tools.wxs
index 8f8418a..7a805d0 100644
--- a/Tools/msi/tools/tools.wxs
+++ b/Tools/msi/tools/tools.wxs
@@ -9,6 +9,7 @@
         <Feature Id="DefaultFeature" AllowAdvertise="no" Title="!(loc.Title)" Description="!(loc.Description)">
             <ComponentGroupRef Id="tools_py" />
             <ComponentGroupRef Id="tools_scripts" />
+            <ComponentGroupRef Id="tools_cat" />
             <ComponentRef Id="OptionalFeature" />
         </Feature>
     </Product>
diff --git a/Tools/msi/tools/tools_files.wxs b/Tools/msi/tools/tools_files.wxs
index 3ae0db2..9c76b1b 100644
--- a/Tools/msi/tools/tools_files.wxs
+++ b/Tools/msi/tools/tools_files.wxs
@@ -13,4 +13,11 @@
             </Component>
         </ComponentGroup>
     </Fragment>
+    <Fragment>
+        <ComponentGroup Id="tools_cat">
+            <Component Id="tools_cat" Directory="Catalogs" Guid="*">
+                <File Name="python_tools.cat" KeyPath="yes" />
+            </Component>
+        </ComponentGroup>
+    </Fragment>
 </Wix>