Add https client ceritificate mapping test (#74)

This commit is contained in:
jhkimnew 2017-02-24 17:52:16 -08:00 коммит произвёл GitHub
Родитель fc580eb0e9
Коммит cc29517ef3
13 изменённых файлов: 1392 добавлений и 91 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -48,4 +48,4 @@ src/AspNetCore/aspnetcore_msg.rc
src/AspNetCore/version.h
.build
AspNetCoreModule.VC.db
*.VC.*db

Просмотреть файл

@ -4,8 +4,8 @@
using AspNetCoreModule.Test.HttpClientHelper;
using Microsoft.Web.Administration;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Management;
using System.ServiceProcess;
using System.Threading;
@ -260,6 +260,40 @@ namespace AspNetCoreModule.Test.Framework
}
}
public void EnableOneToOneClientCertificateMapping(string siteName, string userName, string password, string publicKey)
{
TestUtility.LogInformation("Enable one-to-one client certificate mapping authentication : " + siteName);
using (ServerManager serverManager = GetServerManager())
{
Configuration config = serverManager.GetApplicationHostConfiguration();
ConfigurationSection iisClientCertificateMappingAuthenticationSection = config.GetSection("system.webServer/security/authentication/iisClientCertificateMappingAuthentication", siteName);
// enable iisClientCertificateMappingAuthentication
ConfigurationElementCollection oneToOneMappingsCollection = iisClientCertificateMappingAuthenticationSection.GetCollection("oneToOneMappings");
iisClientCertificateMappingAuthenticationSection["enabled"] = true;
// add a new oneToOne mapping collection item
ConfigurationElement addElement = oneToOneMappingsCollection.CreateElement("add");
addElement["userName"] = userName;
addElement["password"] = password;
addElement["certificate"] = publicKey;
oneToOneMappingsCollection.Add(addElement);
// set sslFlags with SslNegotiateCert
ConfigurationSection accessSection = config.GetSection("system.webServer/security/access", siteName);
accessSection["sslFlags"] = "Ssl, SslNegotiateCert, SslRequireCert";
// disable other authentication to avoid any noise affected by other authentications
ConfigurationSection anonymousAuthenticationSection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", siteName);
anonymousAuthenticationSection["enabled"] = false;
ConfigurationSection windowsAuthenticationSection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", siteName);
windowsAuthenticationSection["enabled"] = false;
serverManager.CommitChanges();
}
}
public void SetCompression(string siteName, bool enabled)
{
TestUtility.LogInformation("Enable Compression : " + siteName);
@ -924,14 +958,205 @@ namespace AspNetCoreModule.Test.Framework
}
}
public void AddBindingToSite(string siteName, string Ip, int Port, string host)
public string CreateSelfSignedCertificateWithMakeCert(string subjectName, string issuerName = null, string extendedKeyUsage = null)
{
string makecertExeFilePath = "makecert.exe";
var makecertExeFilePaths = new string[]
{
Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows Kits", "8.1", "bin", "x64", "makecert.exe"),
Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows Kits", "8.1", "bin", "x86", "makecert.exe"),
Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows Kits", "8.0", "bin", "x64", "makecert.exe"),
Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows Kits", "8.0", "bin", "x86", "makecert.exe"),
Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows SKDs", "Windows", "v7.1A", "bin", "x64", "makecert.exe"),
Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows SKDs", "Windows", "v7.1A", "bin", "makecert.exe")
};
foreach (string item in makecertExeFilePaths)
{
if (File.Exists(item))
{
makecertExeFilePath = item;
break;
}
}
string parameter;
string targetSSLStore = string.Empty;
if (issuerName == null)
{
// if issuer Name is null, you are going to create a root level certificate
parameter = "-r -pe -n \"CN = " + subjectName + "\" -b 12/22/2013 -e 12/23/2020 -ss root -sr localmachine -len 2048 -a sha256";
targetSSLStore = @"Cert:\LocalMachine\Root"; // => -ss root -sr localmachine
}
else
{
// if issuer Name is *not* null, you are going to create a child evel certificate from the given issuer certificate
switch (extendedKeyUsage)
{
// for web server certificate
case "1.3.6.1.5.5.7.3.1":
parameter = "-pe -n \"CN=" + subjectName + "\" -b 12/22/2013 -e 12/23/" + (System.DateTime.Now.Year + 10).ToString() + " -eku " + extendedKeyUsage + " -is root -ir localmachine -in \"" + issuerName + "\" -len 2048 -ss my -sr localmachine -a sha256";
targetSSLStore = @"Cert:\LocalMachine\My"; // => -ss my -sr localmachine
break;
// for client authentication
case "1.3.6.1.5.5.7.3.2":
parameter = "-pe -n \"CN=" + subjectName + "\" -eku " + extendedKeyUsage + " -is root -ir localmachine -in \"" + issuerName + "\" -ss my -sr currentuser -len 2048 -a sha256";
targetSSLStore = @"Cert:\CurrentUser\My"; // => -ss my -sr currentuser
break;
default:
throw new NotImplementedException(extendedKeyUsage);
}
}
try
{
TestUtility.RunCommand(makecertExeFilePath, parameter);
}
catch (Exception ex)
{
TestUtility.LogInformation("Failed to run makecert.exe. Makecert.exe is installed with Visual Studio or SDK. Please make sure setting PATH environment to include the directory path of the makecert.exe file");
throw ex;
}
string toolsPath = Path.Combine(InitializeTestMachine.GetSolutionDirectory(), "tools");
string powershellScript = Path.Combine(toolsPath, "certificate.ps1")
+ " -Command Get-CertificateThumbPrint" +
" -Subject " + subjectName +
" -TargetSSLStore \"" + targetSSLStore + "\"";
if (issuerName != null)
{
powershellScript += " -IssuerName " + issuerName;
}
string output = TestUtility.RunPowershellScript(powershellScript);
if (output.Length != 40)
{
throw new System.ApplicationException("Failed to create a certificate, output: " + output);
}
return output;
}
public string CreateSelfSignedCertificate(string subjectName)
{
string toolsPath = Path.Combine(InitializeTestMachine.GetSolutionDirectory(), "tools");
string powershellScript = Path.Combine(toolsPath, "certificate.ps1")
+ " -Command Create-SelfSignedCertificate"
+ " -Subject " + subjectName;
string output = TestUtility.RunPowershellScript(powershellScript);
if (output.Length != 40)
{
throw new System.ApplicationException("Failed to create a certificate, output: " + output);
}
return output;
}
public string ExportCertificateTo(string thumbPrint, string sslStoreFrom = @"Cert:\LocalMachine\My", string sslStoreTo = @"Cert:\LocalMachine\Root", string pfxPassword = null)
{
string toolsPath = Path.Combine(InitializeTestMachine.GetSolutionDirectory(), "tools");
string powershellScript = Path.Combine(toolsPath, "certificate.ps1") +
" -Command Export-CertificateTo" +
" -TargetThumbPrint " + thumbPrint +
" -TargetSSLStore " + sslStoreFrom +
" -ExportToSSLStore " + sslStoreTo;
if (pfxPassword != null)
{
powershellScript += " -PfxPassword " + pfxPassword;
}
string output = TestUtility.RunPowershellScript(powershellScript);
if (output != string.Empty)
{
throw new System.ApplicationException("Failed to export a certificate to RootCA, output: " + output);
}
return output;
}
public string GetCertificatePublicKey(string thumbPrint, string sslStore = @"Cert:\LocalMachine\My")
{
string toolsPath = Path.Combine(InitializeTestMachine.GetSolutionDirectory(), "tools");
string powershellScript = Path.Combine(toolsPath, "certificate.ps1") +
" -Command Get-CertificatePublicKey" +
" -TargetThumbPrint " + thumbPrint +
" -TargetSSLStore " + sslStore;
string output = TestUtility.RunPowershellScript(powershellScript);
if (output.Length < 500)
{
throw new System.ApplicationException("Failed to get certificate public key, output: " + output);
}
return output;
}
public string DeleteCertificate(string thumbPrint, string sslStore= @"Cert:\LocalMachine\My")
{
string toolsPath = Path.Combine(InitializeTestMachine.GetSolutionDirectory(), "tools");
string powershellScript = Path.Combine(toolsPath, "certificate.ps1") +
" -Command Delete-Certificate" +
" -TargetThumbPrint " + thumbPrint +
" -TargetSSLStore " + sslStore;
string output = TestUtility.RunPowershellScript(powershellScript);
if (output != string.Empty)
{
throw new System.ApplicationException("Failed to delete a certificate (thumbprint: " + thumbPrint + ", output: " + output);
}
return output;
}
public void SetSSLCertificate(int port, string hexIpAddress, string thumbPrint, string sslStore = @"Cert:\LocalMachine\My")
{
// Remove a certificate mapping if it exists
RemoveSSLCertificate(port, hexIpAddress);
// Configure certificate mapping with the newly created certificate
string toolsPath = Path.Combine(InitializeTestMachine.GetSolutionDirectory(), "tools");
string powershellScript = Path.Combine(toolsPath, "httpsys.ps1") +
" -Command Add-SslBinding" +
" -IpAddress " + hexIpAddress +
" -Port " + port.ToString() +
" –Thumbprint \"" + thumbPrint + "\"" +
" -TargetSSLStore " + sslStore;
string output = TestUtility.RunPowershellScript(powershellScript);
if (output != string.Empty)
{
throw new System.ApplicationException("Failed to configure certificate, output: " + output);
}
}
public void RemoveSSLCertificate(int port, string hexIpAddress, string sslStore = @"Cert:\LocalMachine\My")
{
string toolsPath = Path.Combine(InitializeTestMachine.GetSolutionDirectory(), "tools");
string powershellScript = Path.Combine(toolsPath, "httpsys.ps1") +
" -Command Get-SslBinding" +
" -IpAddress " + hexIpAddress +
" -Port " + port.ToString();
string output = TestUtility.RunPowershellScript(powershellScript);
if (output != string.Empty)
{
// Delete a certificate mapping if it exists
powershellScript = Path.Combine(toolsPath, "httpsys.ps1") + " -Command Delete-SslBinding -IpAddress " + hexIpAddress + " -Port " + port.ToString();
output = TestUtility.RunPowershellScript(powershellScript);
if (output != string.Empty)
{
throw new System.ApplicationException("Failed to delete certificate, output: " + output);
}
}
}
public void AddBindingToSite(string siteName, string ipAddress, int port, string host, string protocol = "http")
{
string bindingInfo = "";
if (Ip == null)
Ip = "*";
bindingInfo += Ip;
if (ipAddress == null)
ipAddress = "*";
bindingInfo += ipAddress;
bindingInfo += ":";
bindingInfo += Port;
bindingInfo += port;
bindingInfo += ":";
if (host != null)
bindingInfo += host;
@ -944,7 +1169,7 @@ namespace AspNetCoreModule.Test.Framework
{
SiteCollection sites = serverManager.Sites;
Binding b = sites[siteName].Bindings.CreateElement();
b.SetAttributeValue("protocol", "http");
b.SetAttributeValue("protocol", protocol);
b.SetAttributeValue("bindingInformation", bindingInfo);
sites[siteName].Bindings.Add(b);

Просмотреть файл

@ -16,6 +16,9 @@ using System.Security.AccessControl;
using System.Net.Http;
using System.Threading.Tasks;
using System.Net;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace AspNetCoreModule.Test.Framework
{
@ -216,7 +219,7 @@ namespace AspNetCoreModule.Test.Framework
}
return false;
}
public static void GiveWritePermissionTo(string folder, SecurityIdentifier sid)
{
DirectorySecurity fsecurity = Directory.GetAccessControl(folder);
@ -696,6 +699,48 @@ namespace AspNetCoreModule.Test.Framework
return result;
}
public static string RunPowershellScript(string scriptText)
{
IPEndPoint a = new IPEndPoint(0, 443);
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = null;
try
{
// execute the script
results = pipeline.Invoke();
}
catch (System.Exception ex)
{
throw new Exception("Failed to run " + scriptText + " " + ex.ToString());
}
// close the runspace
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString().Trim(new char[] { ' ', '\r', '\n' });
}
public static int RunCommand(string fileName, string arguments = null, bool checkStandardError = true, bool waitForExit=true)
{
int pid = -1;

Просмотреть файл

@ -101,21 +101,30 @@ namespace AspNetCoreModule.Test.Framework
}
}
public Uri GetHttpUri()
public Uri GetUri()
{
return new Uri("http://" + _testSite.HostName + ":" + _testSite.TcpPort.ToString() + URL);
}
public Uri GetHttpUri(string subPath)
public Uri GetUri(string subPath, int port = -1, string protocol = "http")
{
string tempSubPath = subPath;
if (!tempSubPath.StartsWith("/"))
if (port == -1)
{
tempSubPath = "/" + tempSubPath;
port = _testSite.TcpPort;
}
return new Uri("http://" + _testSite.HostName + ":" + _testSite.TcpPort.ToString() + URL + tempSubPath);
}
string tempSubPath = string.Empty;
if (subPath != null)
{
tempSubPath = subPath;
if (!tempSubPath.StartsWith("/"))
{
tempSubPath = "/" + tempSubPath;
}
}
return new Uri(protocol + "://" + _testSite.HostName + ":" + port.ToString() + URL + tempSubPath);
}
public string _appPoolName = null;
public string AppPoolName
{

Просмотреть файл

@ -66,6 +66,19 @@ namespace AspNetCoreModule.Test.Framework
}
}
public string _postFix = null;
public string PostFix
{
get
{
return _postFix;
}
set
{
_postFix = value;
}
}
public int _tcpPort = 8080;
public int TcpPort
{
@ -103,11 +116,12 @@ namespace AspNetCoreModule.Test.Framework
//
string siteRootPath = string.Empty;
string siteName = string.Empty;
string postfix = string.Empty;
// repeat three times until getting the valid temporary directory path
for (int i = 0; i < 3; i++)
{
string postfix = Path.GetRandomFileName();
postfix = Path.GetRandomFileName();
siteName = loggerPrefix.Replace(" ", "") + "_" + postfix;
siteRootPath = Path.Combine(Environment.ExpandEnvironmentVariables("%SystemDrive%") + @"\", "inetpub", "ANCMTest", siteName);
if (!Directory.Exists(siteRootPath))
@ -174,6 +188,7 @@ namespace AspNetCoreModule.Test.Framework
// Initialize member variables
_hostName = "localhost";
_siteName = siteName;
_postFix = postfix;
_tcpPort = tcpPort;
RootAppContext = new TestWebApplication("/", Path.Combine(siteRootPath, "WebSite1"), this);

Просмотреть файл

@ -289,5 +289,28 @@ namespace AspNetCoreModule.Test
{
return DoCachingTest(appPoolBitness);
}
[EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")]
[ConditionalTheory]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)]
[InlineData(IISConfigUtility.AppPoolBitness.noChange)]
public Task SendHTTPSRequestTest(IISConfigUtility.AppPoolBitness appPoolBitness)
{
return DoSendHTTPSRequestTest(appPoolBitness);
}
[EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")]
[ConditionalTheory]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, true)]
[InlineData(IISConfigUtility.AppPoolBitness.noChange, true)]
[InlineData(IISConfigUtility.AppPoolBitness.noChange, false)]
public Task ClientCertificateMappingTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool useHTTPSMiddleWare)
{
return DoClientCertificateMappingTest(appPoolBitness, useHTTPSMiddleWare);
}
}
}

Просмотреть файл

@ -41,7 +41,7 @@ namespace AspNetCoreModule.Test
DateTime startTime = DateTime.Now;
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.NotEqual(backendProcessId_old, backendProcessId);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
@ -50,7 +50,7 @@ namespace AspNetCoreModule.Test
var httpClientHandler = new HttpClientHandler();
var httpClient = new HttpClient(httpClientHandler)
{
BaseAddress = testSite.AspNetCoreApp.GetHttpUri(),
BaseAddress = testSite.AspNetCoreApp.GetUri(),
Timeout = TimeSpan.FromSeconds(5),
};
@ -73,7 +73,7 @@ namespace AspNetCoreModule.Test
DateTime startTime = DateTime.Now;
Thread.Sleep(1000);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.NotEqual(backendProcessId_old, backendProcessId);
backendProcessId_old = backendProcessId;
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
@ -99,7 +99,7 @@ namespace AspNetCoreModule.Test
DateTime startTime = DateTime.Now;
Thread.Sleep(1000);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.NotEqual(backendProcessId_old, backendProcessId);
backendProcessId_old = backendProcessId;
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
@ -131,7 +131,7 @@ namespace AspNetCoreModule.Test
DateTime startTime = DateTime.Now;
Thread.Sleep(1000);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
Assert.NotEqual(backendProcessId_old, backendProcessId);
backendProcessId_old = backendProcessId;
@ -163,7 +163,7 @@ namespace AspNetCoreModule.Test
Thread.Sleep(500);
string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId";
string backendProcessId = await GetResponse(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.RootAppContext.GetUri(urlForUrlRewrite), HttpStatusCode.OK);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
Assert.NotEqual(backendProcessId_old, backendProcessId);
backendProcessId_old = backendProcessId;
@ -196,7 +196,7 @@ namespace AspNetCoreModule.Test
Thread.Sleep(1000);
string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId";
string backendProcessId = await GetResponse(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.RootAppContext.GetUri(urlForUrlRewrite), HttpStatusCode.OK);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
Assert.NotEqual(backendProcessId_old, backendProcessId);
backendProcessId_old = backendProcessId;
@ -221,8 +221,8 @@ namespace AspNetCoreModule.Test
DateTime startTime = DateTime.Now;
Thread.Sleep(500);
string totalNumber = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetEnvironmentVariables"), HttpStatusCode.OK);
Assert.True(totalNumber == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetEnvironmentVariables"), HttpStatusCode.OK)));
string totalNumber = await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK);
Assert.True(totalNumber == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK)));
iisConfig.SetANCMConfig(
testSite.SiteName,
@ -237,7 +237,7 @@ namespace AspNetCoreModule.Test
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
int expectedValue = Convert.ToInt32(totalNumber) + 1;
Assert.True(expectedValue.ToString() == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetEnvironmentVariables"), HttpStatusCode.OK)));
Assert.True(expectedValue.ToString() == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK)));
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "environmentVariable", new string[] { "ANCMTestBar", "bar" });
Thread.Sleep(500);
@ -245,8 +245,8 @@ namespace AspNetCoreModule.Test
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
expectedValue++;
Assert.True("foo" == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ExpandEnvironmentVariablesANCMTestFoo"), HttpStatusCode.OK)));
Assert.True("bar" == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ExpandEnvironmentVariablesANCMTestBar"), HttpStatusCode.OK)));
Assert.True("foo" == (await GetResponse(testSite.AspNetCoreApp.GetUri("ExpandEnvironmentVariablesANCMTestFoo"), HttpStatusCode.OK)));
Assert.True("bar" == (await GetResponse(testSite.AspNetCoreApp.GetUri("ExpandEnvironmentVariablesANCMTestBar"), HttpStatusCode.OK)));
}
testSite.AspNetCoreApp.RestoreFile("web.config");
@ -270,11 +270,11 @@ namespace AspNetCoreModule.Test
Thread.Sleep(1100);
// verify 503
await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
// rename app_offline.htm to _app_offline.htm and verify 200
testSite.AspNetCoreApp.MoveFile("App_Offline.Htm", "_App_Offline.Htm");
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
Assert.NotEqual(backendProcessId_old, backendProcessId);
@ -305,11 +305,11 @@ namespace AspNetCoreModule.Test
// verify 503
string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId";
await VerifyResponseBody(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
await VerifyResponseBody(testSite.RootAppContext.GetUri(urlForUrlRewrite), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
// delete app_offline.htm and verify 200
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
string backendProcessId = await GetResponse(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.RootAppContext.GetUri(urlForUrlRewrite), HttpStatusCode.OK);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
Assert.NotEqual(backendProcessId_old, backendProcessId);
@ -333,7 +333,7 @@ namespace AspNetCoreModule.Test
new KeyValuePair<string, string>("TestData", testData),
};
var expectedResponseBody = "FirstName=Mickey&LastName=Mouse&TestData=" + testData;
await VerifyPostResponseBody(testSite.AspNetCoreApp.GetHttpUri("EchoPostData"), postFormData, expectedResponseBody, HttpStatusCode.OK);
await VerifyPostResponseBody(testSite.AspNetCoreApp.GetUri("EchoPostData"), postFormData, expectedResponseBody, HttpStatusCode.OK);
}
}
@ -359,7 +359,7 @@ namespace AspNetCoreModule.Test
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "disableStartUpErrorPage", true);
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processPath", errorMessageContainThis);
var responseBody = await GetResponse(testSite.AspNetCoreApp.GetHttpUri(), HttpStatusCode.BadGateway);
var responseBody = await GetResponse(testSite.AspNetCoreApp.GetUri(), HttpStatusCode.BadGateway);
responseBody = responseBody.Replace("\r", "").Replace("\n", "").Trim();
Assert.True(responseBody == curstomErrorMessage);
@ -376,7 +376,7 @@ namespace AspNetCoreModule.Test
// check JitDebugger before continuing
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
responseBody = await GetResponse(testSite.AspNetCoreApp.GetHttpUri(), HttpStatusCode.BadGateway);
responseBody = await GetResponse(testSite.AspNetCoreApp.GetUri(), HttpStatusCode.BadGateway);
Assert.True(responseBody.Contains("808681"));
// verify event error log
@ -409,7 +409,7 @@ namespace AspNetCoreModule.Test
DateTime startTimeInsideLooping = DateTime.Now;
Thread.Sleep(50);
var statusCode = await GetResponseStatusCode(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"));
var statusCode = await GetResponseStatusCode(testSite.AspNetCoreApp.GetUri("GetProcessId"));
if (statusCode != HttpStatusCode.OK.ToString())
{
Assert.True(i >= valueOfRapidFailsPerMinute, i.ToString() + "is greater than or equals to " + valueOfRapidFailsPerMinute.ToString());
@ -418,7 +418,7 @@ namespace AspNetCoreModule.Test
break;
}
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.NotEqual(backendProcessId_old, backendProcessId);
backendProcessId_old = backendProcessId;
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
@ -455,7 +455,7 @@ namespace AspNetCoreModule.Test
for (int i = 0; i < 20; i++)
{
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
int id = Convert.ToInt32(backendProcessId);
if (!processIDs.Contains(id))
{
@ -487,7 +487,7 @@ namespace AspNetCoreModule.Test
for (int i = 0; i < 20; i++)
{
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
int id = Convert.ToInt32(backendProcessId);
if (!processIDs.Contains(id))
{
@ -521,11 +521,11 @@ namespace AspNetCoreModule.Test
Thread.Sleep(500);
if (startupTimeLimit < startupDelay)
{
await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("DoSleep3000"), HttpStatusCode.BadGateway);
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("DoSleep3000"), HttpStatusCode.BadGateway);
}
else
{
await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri("DoSleep3000"), "Running", HttpStatusCode.OK);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep3000"), "Running", HttpStatusCode.OK);
}
}
testSite.AspNetCoreApp.RestoreFile("web.config");
@ -543,11 +543,11 @@ namespace AspNetCoreModule.Test
if (requestTimeout.ToString() == "00:02:00")
{
await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri("DoSleep65000"), "Running", HttpStatusCode.OK, timeout:70);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep65000"), "Running", HttpStatusCode.OK, timeout:70);
}
else if (requestTimeout.ToString() == "00:01:00")
{
await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("DoSleep65000"), HttpStatusCode.BadGateway, 70);
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("DoSleep65000"), HttpStatusCode.BadGateway, 70);
}
else
{
@ -573,8 +573,8 @@ namespace AspNetCoreModule.Test
new string[] { "ANCMTestShutdownDelay", "20000" }
);
await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
// Set a new value such as 100 to make the backend process being recycled
@ -585,8 +585,8 @@ namespace AspNetCoreModule.Test
var difference = endTime - startTime;
Assert.True(difference.Seconds >= expectedClosingTime);
Assert.True(difference.Seconds < expectedClosingTime + 3);
Assert.True(backendProcessId != await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK));
await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK);
Assert.True(backendProcessId != await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK));
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
}
testSite.AspNetCoreApp.RestoreFile("web.config");
@ -605,7 +605,7 @@ namespace AspNetCoreModule.Test
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogEnabled", true);
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogFile", @".\logs\stdout");
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
string logPath = testSite.AspNetCoreApp.GetDirectoryPathWith("logs");
Assert.False(Directory.Exists(logPath));
Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), 1004, startTime, @"logs\stdout"));
@ -615,7 +615,7 @@ namespace AspNetCoreModule.Test
// verify the log file is not created because backend process is not recycled
Assert.True(Directory.GetFiles(logPath).Length == 0);
Assert.True(backendProcessId == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK)));
Assert.True(backendProcessId == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK)));
// reset web.config to recycle backend process and give write permission to the Users local group to which IIS workerprocess identity belongs
SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
@ -630,7 +630,7 @@ namespace AspNetCoreModule.Test
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogEnabled", true);
Assert.True(backendProcessId != (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK)));
Assert.True(backendProcessId != (await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK)));
// Verify log file is created now after backend process is recycled
Assert.True(TestUtility.RetryHelper(p => { return Directory.GetFiles(p).Length > 0 ? true : false; }, logPath));
@ -647,7 +647,7 @@ namespace AspNetCoreModule.Test
using (var iisConfig = new IISConfigUtility(ServerType.IIS))
{
string arguments = argumentsPrefix + testSite.AspNetCoreApp.GetArgumentFileName();
string tempProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string tempProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
var tempBackendProcess = Process.GetProcessById(Convert.ToInt32(tempProcessId));
// replace $env with the actual test value
@ -669,7 +669,7 @@ namespace AspNetCoreModule.Test
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
Thread.Sleep(500);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
}
@ -685,7 +685,7 @@ namespace AspNetCoreModule.Test
{
string result = string.Empty;
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "forwardWindowsAuthToken", enabledForwardWindowsAuthToken);
string requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("DumpRequestHeaders"), HttpStatusCode.OK);
string requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetUri("DumpRequestHeaders"), HttpStatusCode.OK);
Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN"));
iisConfig.EnableWindowsAuthentication(testSite.SiteName);
@ -696,13 +696,13 @@ namespace AspNetCoreModule.Test
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
Thread.Sleep(500);
requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("DumpRequestHeaders"), HttpStatusCode.OK);
requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetUri("DumpRequestHeaders"), HttpStatusCode.OK);
if (enabledForwardWindowsAuthToken)
{
string expectedHeaderName = "MS-ASPNETCORE-WINAUTHTOKEN";
Assert.True(requestHeaders.ToUpper().Contains(expectedHeaderName));
result = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ImpersonateMiddleware"), HttpStatusCode.OK);
result = await GetResponse(testSite.AspNetCoreApp.GetUri("ImpersonateMiddleware"), HttpStatusCode.OK);
bool compare = false;
string expectedValue1 = "ImpersonateMiddleware-UserName = " + Environment.ExpandEnvironmentVariables("%USERDOMAIN%") + "\\" + Environment.ExpandEnvironmentVariables("%USERNAME%");
@ -723,7 +723,7 @@ namespace AspNetCoreModule.Test
{
Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN"));
result = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ImpersonateMiddleware"), HttpStatusCode.OK);
result = await GetResponse(testSite.AspNetCoreApp.GetUri("ImpersonateMiddleware"), HttpStatusCode.OK);
Assert.True(result.Contains("ImpersonateMiddleware-UserName = NoAuthentication"));
}
}
@ -740,10 +740,10 @@ namespace AspNetCoreModule.Test
{
// allocating 1024,000 KB
await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("MemoryLeak1024000"), HttpStatusCode.OK);
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("MemoryLeak1024000"), HttpStatusCode.OK);
// get backend process id
string pocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string pocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
// get process id of IIS worker process (w3wp.exe)
string userName = testSite.SiteName;
@ -777,7 +777,7 @@ namespace AspNetCoreModule.Test
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "rapidFailsPerMinute", 100);
Thread.Sleep(3000);
await VerifyResponseStatus(testSite.RootAppContext.GetHttpUri("small.htm"), HttpStatusCode.OK);
await VerifyResponseStatus(testSite.RootAppContext.GetUri("small.htm"), HttpStatusCode.OK);
Thread.Sleep(1000);
int x = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
@ -788,14 +788,14 @@ namespace AspNetCoreModule.Test
// check JitDebugger before continuing
foundVSJit = TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
await VerifyResponseStatus(testSite.RootAppContext.GetHttpUri("small.htm"), HttpStatusCode.OK);
await VerifyResponseStatus(testSite.RootAppContext.GetUri("small.htm"), HttpStatusCode.OK);
Thread.Sleep(3000);
}
int y = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
Assert.True(x == y && foundVSJit == false, "worker process is not recycled after 30 seconds");
string backupPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backupPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
string newPocessIdBackendProcess = backupPocessIdBackendProcess;
// Verify IIS recycling happens while there is memory leak
@ -805,9 +805,9 @@ namespace AspNetCoreModule.Test
foundVSJit = TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
// allocating 2048,000 KB
await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("MemoryLeak2048000"), HttpStatusCode.OK);
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("MemoryLeak2048000"), HttpStatusCode.OK);
newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
if (foundVSJit || backupPocessIdBackendProcess != newPocessIdBackendProcess)
{
// worker process is recycled expectedly and backend process is recycled together
@ -834,7 +834,7 @@ namespace AspNetCoreModule.Test
z = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
Assert.True(x != z, "worker process is recycled");
newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.True(backupPocessIdBackendProcess != newPocessIdBackendProcess, "backend process is recycled");
}
testSite.AspNetCoreApp.RestoreFile("web.config");
@ -876,29 +876,29 @@ namespace AspNetCoreModule.Test
string result = string.Empty;
if (!useCompressionMiddleWare && !enableIISCompression)
{
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("foohtm"), "verify response body");
Assert.False(result.Contains("Content-Encoding"), "verify response header");
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("barhtm"), "verify response body");
Assert.False(result.Contains("Content-Encoding"), "verify response header");
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("defaulthtm"), "verify response body");
Assert.False(result.Contains("Content-Encoding"), "verify response header");
}
else
{
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("foohtm"), "verify response body");
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("barhtm"), "verify response body");
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("defaulthtm"), "verify response body");
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
}
@ -937,20 +937,20 @@ namespace AspNetCoreModule.Test
string result = string.Empty;
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
string headerValue = GetHeaderValue(result, "MyCustomHeader");
Assert.True(result.Contains("foohtm"), "verify response body");
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
Thread.Sleep(2000);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
string headerValue2 = GetHeaderValue(result, "MyCustomHeader");
Assert.True(result.Contains("foohtm"), "verify response body");
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
Assert.Equal(headerValue, headerValue2);
Thread.Sleep(12000);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("foohtm"), "verify response body");
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
string headerValue3 = GetHeaderValue(result, "MyCustomHeader");
@ -959,23 +959,184 @@ namespace AspNetCoreModule.Test
testSite.AspNetCoreApp.RestoreFile("web.config");
}
}
public static async Task DoSendHTTPSRequestTest(IISConfigUtility.AppPoolBitness appPoolBitness)
{
using (var testSite = new TestWebSite(appPoolBitness, "DoSendHTTPSRequestTest"))
{
using (var iisConfig = new IISConfigUtility(ServerType.IIS))
{
string hostName = "";
string subjectName = "localhost";
string ipAddress = "*";
string hexIPAddress = "0x00";
int sslPort = 46300;
// Add https binding and get https uri information
iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https");
// Create a self signed certificate
string thumbPrint = iisConfig.CreateSelfSignedCertificate(subjectName);
// Export the self signed certificate to rootCA
iisConfig.ExportCertificateTo(thumbPrint, sslStoreTo:@"Cert:\LocalMachine\Root");
// Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage
iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrint);
// Verify http request
string result = string.Empty;
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("Running"), "verify response body");
// Verify https request
Uri targetHttpsUri = testSite.AspNetCoreApp.GetUri(null, sslPort, protocol: "https");
result = await GetResponseAndHeaders(targetHttpsUri, new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("Running"), "verify response body");
// Remove the SSL Certificate mapping
iisConfig.RemoveSSLCertificate(sslPort, hexIPAddress);
// Remove the newly created self signed certificate
iisConfig.DeleteCertificate(thumbPrint);
// Remove the exported self signed certificate on rootCA
iisConfig.DeleteCertificate(thumbPrint, @"Cert:\LocalMachine\Root");
}
testSite.AspNetCoreApp.RestoreFile("web.config");
}
}
public static async Task DoClientCertificateMappingTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool useHTTPSMiddleWare)
{
using (var testSite = new TestWebSite(appPoolBitness, "DoClientCertificateMappingTest"))
{
using (var iisConfig = new IISConfigUtility(ServerType.IIS))
{
string hostName = "";
string rootCN = "ANCMTest" + testSite.PostFix;
string webServerCN = "localhost";
string kestrelServerCN = "localhost";
string clientCN = "ANCMClient-" + testSite.PostFix;
string ipAddress = "*";
string hexIPAddress = "0x00";
int sslPort = 46300;
// Add https binding and get https uri information
iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https");
// Create a root certificate
string thumbPrintForRoot = iisConfig.CreateSelfSignedCertificateWithMakeCert(rootCN);
// Create a certificate for web server setting its issuer with the root certificate subject name
string thumbPrintForWebServer = iisConfig.CreateSelfSignedCertificateWithMakeCert(webServerCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.1");
string thumbPrintForKestrel = null;
// Create a certificate for client authentication setting its issuer with the root certificate subject name
string thumbPrintForClientAuthentication = iisConfig.CreateSelfSignedCertificateWithMakeCert(clientCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.2");
// Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage
iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrintForWebServer);
// Create a new local administrator user
string userName = "tempuser" + TestUtility.RandomString(5);
string password = "AncmTest123!";
string temp = TestUtility.RunPowershellScript("( Get-LocalUser -Name " + userName + " 2> $null ).Name");
if (temp == userName)
{
temp = TestUtility.RunPowershellScript("net localgroup administrators /Delete " + userName);
temp = TestUtility.RunPowershellScript("net user " + userName + " /Delete");
}
temp = TestUtility.RunPowershellScript("net user " + userName + " " + password + " /ADD");
temp = TestUtility.RunPowershellScript("net localgroup administrators /Add " + userName);
// Get public key of the client certificate and Configure OnetToOneClientCertificateMapping the public key and disable anonymous authentication and set SSL flags for Client certificate authentication
string publicKey = iisConfig.GetCertificatePublicKey(thumbPrintForClientAuthentication, @"Cert:\CurrentUser\My");
iisConfig.EnableOneToOneClientCertificateMapping(testSite.SiteName, ".\\" + userName, password, publicKey);
// Configure kestrel SSL test environment
if (useHTTPSMiddleWare)
{
// set startup class
string startupClass = "StartupHTTPS";
iisConfig.SetANCMConfig(
testSite.SiteName,
testSite.AspNetCoreApp.Name,
"environmentVariable",
new string[] { "ANCMTestStartupClassName", startupClass }
);
// Create a certificate for Kestrel web server and export to TestResources\testcert.pfx
// NOTE: directory name "TestResources", file name "testcert.pfx" and password "testPassword" should be matched to AspnetCoreModule.TestSites.Standard web application
thumbPrintForKestrel = iisConfig.CreateSelfSignedCertificateWithMakeCert(kestrelServerCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.1");
testSite.AspNetCoreApp.CreateDirectory("TestResources");
string pfxFilePath = Path.Combine(testSite.AspNetCoreApp.GetDirectoryPathWith("TestResources"), "testcert.pfx");
iisConfig.ExportCertificateTo(thumbPrintForKestrel, sslStoreFrom: "Cert:\\LocalMachine\\My", sslStoreTo: pfxFilePath, pfxPassword: "testPassword");
Assert.True(File.Exists(pfxFilePath));
}
// Verify http request with using client certificate
Uri targetHttpsUri = testSite.AspNetCoreApp.GetUri(null, sslPort, protocol: "https");
string statusCode = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUri.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").StatusCode");
Assert.Equal("200", statusCode);
// Verify https request with client certificate includes the certificate header "MS-ASPNETCORE-CLIENTCERT"
Uri targetHttpsUriForDumpRequestHeaders = testSite.AspNetCoreApp.GetUri("DumpRequestHeaders", sslPort, protocol: "https");
string outputRawContent = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUriForDumpRequestHeaders.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").RawContent.ToString()");
string expectedHeaderName = "MS-ASPNETCORE-CLIENTCERT";
Assert.True(outputRawContent.Contains(expectedHeaderName));
// Get the value of MS-ASPNETCORE-CLIENTCERT request header again and verify it is matched to its configured public key
Uri targetHttpsUriForCLIENTCERTRequestHeader = testSite.AspNetCoreApp.GetUri("GetRequestHeaderValueMS-ASPNETCORE-CLIENTCERT", sslPort, protocol: "https");
outputRawContent = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUriForCLIENTCERTRequestHeader.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").RawContent.ToString()");
Assert.True(outputRawContent.Contains(publicKey));
// Verify non-https request returns 403.4 error
string result = string.Empty;
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.Forbidden);
Assert.True(result.Contains("403.4"));
// Verify https request without using client certificate returns 403.7
result = await GetResponseAndHeaders(targetHttpsUri, new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.Forbidden);
Assert.True(result.Contains("403.7"));
// Clean up user
temp = TestUtility.RunPowershellScript("net localgroup administrators /Delete " + userName);
temp = TestUtility.RunPowershellScript("net user " + userName + " /Delete");
// Remove the SSL Certificate mapping
iisConfig.RemoveSSLCertificate(sslPort, hexIPAddress);
// Clean up certificates
iisConfig.DeleteCertificate(thumbPrintForRoot, @"Cert:\LocalMachine\Root");
iisConfig.DeleteCertificate(thumbPrintForWebServer, @"Cert:\LocalMachine\My");
if (useHTTPSMiddleWare)
{
iisConfig.DeleteCertificate(thumbPrintForKestrel, @"Cert:\LocalMachine\My");
}
iisConfig.DeleteCertificate(thumbPrintForClientAuthentication, @"Cert:\CurrentUser\My");
}
testSite.AspNetCoreApp.RestoreFile("web.config");
}
}
public static async Task DoWebSocketTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData)
{
using (var testSite = new TestWebSite(appPoolBitness, "DoWebSocketTest"))
{
DateTime startTime = DateTime.Now;
await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
// Get Process ID
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
// Verify WebSocket without setting subprotocol
await VerifyResponseBodyContain(testSite.WebSocketApp.GetHttpUri("echo.aspx"), new string[] { "Socket Open" }, HttpStatusCode.OK); // echo.aspx has hard coded path for the websocket server
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echo.aspx"), new string[] { "Socket Open" }, HttpStatusCode.OK); // echo.aspx has hard coded path for the websocket server
// Verify WebSocket subprotocol
await VerifyResponseBodyContain(testSite.WebSocketApp.GetHttpUri("echoSubProtocol.aspx"), new string[] { "Socket Open", "mywebsocketsubprotocol" }, HttpStatusCode.OK); // echoSubProtocol.aspx has hard coded path for the websocket server
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echoSubProtocol.aspx"), new string[] { "Socket Open", "mywebsocketsubprotocol" }, HttpStatusCode.OK); // echoSubProtocol.aspx has hard coded path for the websocket server
// Verify process creation ANCM event log
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
@ -983,7 +1144,7 @@ namespace AspNetCoreModule.Test
// Verify websocket
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetHttpUri("websocket"), true, true);
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.True(frameReturned.Content.Contains("Connection: Upgrade"));
Assert.True(frameReturned.Content.Contains("HTTP/1.1 101 Switching Protocols"));
Thread.Sleep(500);
@ -996,7 +1157,7 @@ namespace AspNetCoreModule.Test
}
// send a simple request again and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
}
}
@ -1129,7 +1290,7 @@ namespace AspNetCoreModule.Test
private static async Task CheckChunkedAsync(HttpClient client, TestWebApplication webApp)
{
var response = await client.GetAsync(webApp.GetHttpUri("chunked"));
var response = await client.GetAsync(webApp.GetUri("chunked"));
var responseText = await response.Content.ReadAsStringAsync();
try
{
@ -1256,14 +1417,14 @@ namespace AspNetCoreModule.Test
string responseStatus = "NotInitialized";
var httpClientHandler = new HttpClientHandler();
httpClientHandler.UseDefaultCredentials = true;
httpClientHandler.UseDefaultCredentials = true;
var httpClient = new HttpClient(httpClientHandler)
{
BaseAddress = uri,
Timeout = TimeSpan.FromSeconds(timeout),
};
if (requestHeaders != null)
{
for (int i = 0; i < requestHeaders.Length; i=i+2)
@ -1293,7 +1454,7 @@ namespace AspNetCoreModule.Test
else
{
response = await TestUtility.RetryRequest(() =>
{
{
return httpClient.PostAsync(string.Empty, postHttpContent);
}, TestUtility.Logger, retryCount: numberOfRetryCount);
}

Просмотреть файл

@ -16,7 +16,8 @@
"Microsoft.Extensions.Logging.Console": "1.2.0-*",
"Microsoft.Extensions.PlatformAbstractions": "1.2.0-*",
"Microsoft.Net.Http.Headers": "1.1.0-*",
"xunit": "2.2.0-*"
"xunit": "2.2.0-*",
"System.Management.Automation.dll": "10.0.10586"
},
"frameworks": {
"net451": {
@ -28,7 +29,7 @@
"System.Net.Http.WebRequest": "",
"System.Runtime": "",
"System.ServiceProcess": "",
"System.Xml": ""
"System.Xml": ""
}
}
}

Просмотреть файл

@ -2,16 +2,20 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Extensions.Configuration;
using Microsoft.Net.Http.Server;
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
namespace AspnetCoreModule.TestSites.Standard
{
public static class Program
{
private static X509Certificate2 _x509Certificate2;
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
@ -22,7 +26,33 @@ namespace AspnetCoreModule.TestSites.Standard
IWebHostBuilder builder = null;
if (!string.IsNullOrEmpty(startUpClassString))
{
if (startUpClassString == "StartupCompressionCaching")
if (startUpClassString == "StartupHTTPS")
{
// load .\testresources\testcert.pfx
string pfxPassword = "testPassword";
if (File.Exists(@".\TestResources\testcert.pfx"))
{
_x509Certificate2 = new X509Certificate2(@".\TestResources\testcert.pfx", pfxPassword);
}
else
{
throw new Exception(@"Certificate file not found: .\TestResources\testcert.pfx of which password should " + pfxPassword);
}
builder = new WebHostBuilder()
.UseConfiguration(config)
.UseIISIntegration()
.UseKestrel(options =>
{
HttpsConnectionFilterOptions httpsoptions = new HttpsConnectionFilterOptions();
httpsoptions.ServerCertificate = _x509Certificate2;
httpsoptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
httpsoptions.CheckCertificateRevocation = false;
options.UseHttps(httpsoptions);
})
.UseStartup<Startup>();
}
else if (startUpClassString == "StartupCompressionCaching")
{
builder = new WebHostBuilder()
.UseConfiguration(config)
@ -111,8 +141,8 @@ namespace AspnetCoreModule.TestSites.Standard
}
var host = builder.Build();
host.Run();
if (Startup.SleeptimeWhileClosing != 0)
{
Thread.Sleep(Startup.SleeptimeWhileClosing);

Просмотреть файл

@ -4,15 +4,16 @@
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:39982/",
"sslPort": 0
"sslPort": 44375
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "https://localhost:44375/",
"environmentVariables": {
"ANCMTestStartupClassName": "StartupCompression",
"ANCMTestStartupClassName": "StartupHTTPS",
"ASPNET_ENVIRONMENT": "HelloWorld"
}
},

Двоичный файл не отображается.

397
tools/certificate.ps1 Normal file
Просмотреть файл

@ -0,0 +1,397 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
<##############################################################################
Example
###############################################################################
# Create a new root certificate on "Cert:\LocalMachine\My" and export it to "Cert:\LocalMachine\Root"
# FYI, you can do the same thing with one of the following commands:
# %sdxroot\tools\amd64\MakeCert.exe -r -pe -n "CN=ANCMTest_Root" -b 12/22/2013 -e 12/23/2020 -ss root -sr localmachine -len 2048 -a sha256
# $thumbPrint = (New-SelfSignedCertificate -DnsName "ANCMTest_Root", "ANCMTest_Roo3" -CertStoreLocation "cert:\LocalMachine\My").Thumbprint
###############################################################################
$rootSubject = "ANCMTest_Root"
$thumbPrint = .\certificate.ps1 -Command Create-SelfSignedCertificate -Subject $rootSubject
.\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint $thumbPrint -TargetSSLStore "Cert:\LocalMachine\My" -ExportToSSLStore "Cert:\LocalMachine\Root"
.\certificate.ps1 -Command Get-CertificateThumbPrint -Subject $rootSubject -TargetSSLStore "Cert:\LocalMachine\Root"
###############################################################################
# Create a new certificate setting issuer with the root certicate's subject name on "Cert:\LocalMachine\My" and export it to "Cert:\LocalMachine\Root"
# FYI, you can do the same thing with one of the following commands:
# %sdxroot\tools\amd64\MakeCert.exe -pe -n "CN=ANCMTestWebServer" -b 12/22/2013 -e 12/23/2020 -eku 1.3.6.1.5.5.7.3.1 -is root -ir localmachine -in $rootSubject -len 2048 -ss my -sr localmachine -a sha256
# %sdxroot\tools\amd64\MakeCert.exe -pe -n "CN=ANCMTest_Client" -eku 1.3.6.1.5.5.7.3.2 -is root -ir localmachine -in ANCMTest_Root -ss my -sr currentuser -len 2048 -a sha256
###############################################################################
$childSubject = "ANCMTest_Client"
$thumbPrint2 = .\certificate.ps1 -Command Create-SelfSignedCertificate -Subject $childSubject -IssuerName $rootSubject
("Result: $thumbPrint2")
.\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\LocalMachine\My" -ExportToSSLStore "Cert:\CurrentUser\My"
.\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint F97AB75DCF1C62547E4B5E7025D60001892A6A60 -ExportToSSLStore C:\gitroot\AspNetCoreModule\tools\test.pfx -PfxPassword test
# Clean up
.\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\LocalMachine\My"
.\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\CurrentUser\Root"
.\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint -TargetSSLStore "Cert:\LocalMachine\My"
.\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint -TargetSSLStore "Cert:\LocalMachine\Root"
###############################################################################>
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Create-SelfSignedCertificate",
"Delete-Certificate",
"Export-CertificateTo",
"Get-CertificateThumbPrint",
"Get-CertificatePublicKey")]
[string]
$Command,
[parameter()]
[string]
$Subject,
[parameter()]
[string]
$IssuerName,
[Parameter()]
[string]
$FriendlyName = "",
[Parameter()]
[string[]]
$AlternativeNames = "",
[Parameter()]
[string]
$TargetSSLStore = "",
[Parameter()]
[string]
$ExportToSSLStore = "",
[Parameter()]
[string]
$PfxPassword = "",
[Parameter()]
[string]
$TargetThumbPrint = ""
)
function Create-SelfSignedCertificate($_subject, $_friendlyName, $_alternativeNames, $_issuerName) {
if (-not $_subject)
{
return ("Error!!! _subject is required")
}
#
# $_issuerName should be set with the value subject and its certificate path will be root path
if (-not $_issuerName)
{
$_issuerName = $_subject
}
#
# Create $subjectDn and $issuerDn
$subjectDn = new-object -com "X509Enrollment.CX500DistinguishedName"
$subjectDn.Encode( "CN=" + $_subject, $subjectDn.X500NameFlags.X500NameFlags.XCN_CERT_NAME_STR_NONE)
$issuerDn = new-object -com "X509Enrollment.CX500DistinguishedName"
$issuerDn.Encode("CN=" + $_issuerName, $subjectDn.X500NameFlags.X500NameFlags.XCN_CERT_NAME_STR_NONE)
#
# Create a new Private Key
$key = new-object -com "X509Enrollment.CX509PrivateKey"
$key.ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
# XCN_AT_SIGNATURE, The key can be used for signing
$key.KeySpec = 2
$key.Length = 2048
# MachineContext 0: Current User, 1: Local Machine
$key.MachineContext = 1
$key.Create()
#
# Create a cert object with the newly created private key
$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate"
$cert.InitializeFromPrivateKey(2, $key, "")
$cert.Subject = $subjectDn
$cert.Issuer = $issuerDn
$cert.NotBefore = (get-date).AddMinutes(-10)
$cert.NotAfter = $cert.NotBefore.AddYears(2)
#Use Sha256
$hashAlgorithm = New-Object -ComObject X509Enrollment.CObjectId
$hashAlgorithm.InitializeFromAlgorithmName(1,0,0,"SHA256")
$cert.HashAlgorithm = $hashAlgorithm
#
# Key usage should be set for non-root certificate
if ($_issuerName -ne $_subject)
{
#
# Extended key usage
$clientAuthOid = New-Object -ComObject "X509Enrollment.CObjectId"
$clientAuthOid.InitializeFromValue("1.3.6.1.5.5.7.3.2")
$serverAuthOid = new-object -com "X509Enrollment.CObjectId"
$serverAuthOid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
$ekuOids = new-object -com "X509Enrollment.CObjectIds.1"
$ekuOids.add($clientAuthOid)
$ekuOids.add($serverAuthOid)
$ekuExt = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage"
$ekuExt.InitializeEncode($ekuOids)
$cert.X509Extensions.Add($ekuext)
#
#Set Key usage
$keyUsage = New-Object -com "X509Enrollment.cx509extensionkeyusage"
# XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE
$flags = 0x20
# XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE
$flags = $flags -bor 0x80
$keyUsage.InitializeEncode($flags)
$cert.X509Extensions.Add($keyUsage)
}
#
# Subject alternative names
if ($_alternativeNames -ne $null) {
$names = new-object -com "X509Enrollment.CAlternativeNames"
$altNames = new-object -com "X509Enrollment.CX509ExtensionAlternativeNames"
foreach ($n in $_alternativeNames) {
$name = new-object -com "X509Enrollment.CAlternativeName"
# Dns Alternative Name
$name.InitializeFromString(3, $n)
$names.Add($name)
}
$altNames.InitializeEncode($names)
$cert.X509Extensions.Add($altNames)
}
$cert.Encode()
#$locator = $(New-Object "System.Guid").ToString()
$locator = [guid]::NewGuid().ToString()
$enrollment = new-object -com "X509Enrollment.CX509Enrollment"
$enrollment.CertificateFriendlyName = $locator
$enrollment.InitializeFromRequest($cert)
$certdata = $enrollment.CreateRequest(0)
$enrollment.InstallResponse(2, $certdata, 0, "")
# Wait for certificate to be populated
$end = $(Get-Date).AddSeconds(1)
do {
$Certificates = Get-ChildItem Cert:\LocalMachine\My
foreach ($item in $Certificates)
{
if ($item.FriendlyName -eq $locator)
{
$CACertificate = $item
}
}
} while ($CACertificate -eq $null -and $(Get-Date) -lt $end)
$thumbPrint = ""
if ($CACertificate -and $CACertificate.Thumbprint)
{
$thumbPrint = $CACertificate.Thumbprint.Trim()
}
return $thumbPrint
}
function Delete-Certificate($_targetThumbPrint, $_targetSSLStore = $TargetSSLStore) {
if (-not $_targetThumbPrint)
{
return ("Error!!! _targetThumbPrint is required")
}
if (Test-Path "$_targetSSLStore\$_targetThumbPrint")
{
Remove-Item "$_targetSSLStore\$_targetThumbPrint" -Force -Confirm:$false
}
if (Test-Path "$_targetSSLStore\$_targetThumbPrint")
{
return ("Error!!! Failed to delete a certificate of $_targetThumbPrint")
}
}
function Export-CertificateTo($_targetThumbPrint, $_exportToSSLStore, $_password)
{
if (-not $_targetThumbPrint)
{
return ("Error!!! _targetThumbPrint is required")
}
if (-not (Test-Path "$TargetSSLStore\$_targetThumbPrint"))
{
return ("Error!!! Export failed. Can't find target certificate: $TargetSSLStore\$_targetThumbPrint")
}
$cert = Get-Item "$TargetSSLStore\$_targetThumbPrint"
$tempExportFile = "$env:temp\_tempCertificate.cer"
if (Test-Path $tempExportFile)
{
Remove-Item $tempExportFile -Force -Confirm:$false
}
# if _exportToSSLStore points to a .pfx file
if ($exportToSSLStore.ToLower().EndsWith(".pfx"))
{
if (-not $_password)
{
return ("Error!!! _password is required")
}
$securedPassword = ConvertTo-SecureString -String $_password -Force AsPlainText
$exportedPfxFile = Export-PfxCertificate -FilePath $_exportToSSLStore -Cert $TargetSSLStore\$_targetThumbPrint -Password $securedPassword
if ( ($exportedPfxFile -ne $null) -and (Test-Path $exportedPfxFile.FullName) )
{
# Succeeded to export to .pfx file
return
}
else
{
return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile")
}
}
Export-Certificate -Cert $cert -FilePath $tempExportFile | Out-Null
if (-not (Test-Path $tempExportFile))
{
return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile")
}
# clean up destination SSL store
Delete-Certificate $_targetThumbPrint $_exportToSSLStore
if (Test-Path "$_exportToSSLStore\$_targetThumbPrint")
{
return ("Error!!! Can't delete already existing one $_exportToSSLStore\$_targetThumbPrint")
}
Import-Certificate -CertStoreLocation $_exportToSSLStore -FilePath $tempExportFile | Out-Null
if (-not (Test-Path "$_exportToSSLStore\$_targetThumbPrint"))
{
return ("Error!!! Can't copy $TargetSSLStore\$_targetThumbPrint to $_exportToSSLStore")
}
}
function Get-CertificateThumbPrint($_subject, $_issuerName, $_targetSSLStore)
{
if (-not $_subject)
{
return ("Error!!! _subject is required")
}
if (-not $_targetSSLStore)
{
return ("Error!!! _targetSSLStore is required")
}
if (-not (Test-Path "$_targetSSLStore"))
{
return ("Error!!! Can't find target store")
}
$targetCertificate = $null
$Certificates = Get-ChildItem $_targetSSLStore
foreach ($item in $Certificates)
{
$findItem = $false
# check subject name first
if ($item.Subject.ToLower() -eq "CN=$_subject".ToLower())
{
$findItem = $true
}
# check issuerName as well
if ($_issuerName -and $item.Issuer.ToLower() -ne "CN=$_issuerName".ToLower())
{
$findItem = $false
}
if ($findItem)
{
$targetCertificate = $item
break
}
}
$result = ""
if ($targetCertificate)
{
$result = $targetCertificate.Thumbprint
}
else
{
("Error!!! Can't find target certificate")
}
return $result
}
function Get-CertificatePublicKey($_targetThumbPrint)
{
if (-not $_targetThumbPrint)
{
return ("Error!!! _targetThumbPrint is required")
}
if (-not (Test-Path "$TargetSSLStore\$_targetThumbPrint"))
{
return ("Error!!! Can't find target certificate")
}
$cert = Get-Item "$TargetSSLStore\$_targetThumbPrint"
$byteArray = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
$publicKey = [System.Convert]::ToBase64String($byteArray).Trim()
return $publicKey
}
# Error handling and initializing default values
if (-not $TargetSSLStore)
{
$TargetSSLStore = "Cert:\LocalMachine\My"
}
else
{
if ($Command -eq "Create-SelfSignedCertificate")
{
return ("Error!!! Create-SelfSignedCertificate should use default value for -TargetSSLStore if -Issuer is not provided")
}
}
if (-not $ExportToSSLStore)
{
$ExportToSSLStore = "Cert:\LocalMachine\Root"
}
switch ($Command)
{
"Create-SelfSignedCertificate"
{
return Create-SelfSignedCertificate $Subject $FriendlyName $AlternativeNames $IssuerName
}
"Delete-Certificate"
{
return Delete-Certificate $TargetThumbPrint
}
"Export-CertificateTo"
{
return Export-CertificateTo $TargetThumbPrint $ExportToSSLStore $PfxPassword
}
"Get-CertificateThumbPrint"
{
return Get-CertificateThumbPrint $Subject $IssuerName $TargetSSLStore
}
"Get-CertificatePublicKey"
{
return Get-CertificatePublicKey $TargetThumbPrint
}
default
{
throw "Unknown command"
}
}

394
tools/httpsys.ps1 Normal file
Просмотреть файл

@ -0,0 +1,394 @@
# Copyright (c) .NET Foundation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
##############################################################################
# Example
# $result = .\httpsys.ps1 -Command Get-SslBinding -IpAddress "0x00" -Port 46300
# .\httpsys.ps1 -Command Add-SslBinding -IpAddress "0x00" -Port 46300 –Thumbprint $result.CertificateHash
# .\httpsys.ps1 -Command Delete-SslBinding -IpAddress "0x00" -Port 46300
##############################################################################
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Add-SslBinding",
"Delete-SslBinding",
"Get-SslBinding")]
[string]
$Command,
[parameter()]
[string]
$IpAddress,
[parameter()]
[string]
$Port,
[parameter()]
[string]
$Thumbprint,
[parameter()]
[string]
$TargetSSLStore,
[parameter()]
[string]
$AppId,
[parameter()]
[System.Net.IPEndPoint]
$IpEndPoint
)
# adjust parameter variables
if (-not $IpEndPoint)
{
if ($IpAddress -and $Port)
{
$IpEndPoint = New-Object "System.Net.IPEndPoint" -ArgumentList $IpAddress,$Port
}
}
if (-not $TargetSSLStore)
{
$TargetSSLStore = "Cert:\LocalMachine\My"
}
$StoreName = ($TargetSSLStore.Split("\") | Select-Object -Last 1).Trim()
$Certificate = Get-Item "$TargetSSLStore\$Thumbprint"
if (-not $AppId)
{
# Assign a random GUID for $AppId
$AppId = [guid]::NewGuid()
}
$cs = '
namespace Microsoft.IIS.Administration.Setup {
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.ComponentModel;
public class Http {
public const int HTTP_INITIALIZE_CONFIG = 2;
public const int HTTP_SERVICE_CONFIG_SSLCERT_INFO = 1;
[DllImport("httpapi.dll", CharSet = CharSet.Auto, PreserveSig = true)]
public static extern uint HttpDeleteServiceConfiguration(IntPtr ServiceHandle, int ConfigId, ref HTTP_SERVICE_CONFIG_SSL_SET pConfigInformation, int ConfigInformationLength, IntPtr pOverlapped);
[DllImport("httpapi.dll", CharSet = CharSet.Auto, PreserveSig = true)]
public static extern uint HttpInitialize(HTTPAPI_VERSION version, uint flags, IntPtr pReserved);
[DllImport("httpapi.dll", EntryPoint = "HttpQueryServiceConfiguration",
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern uint HttpQueryServiceConfiguration(
IntPtr serviceHandle,
HTTP_SERVICE_CONFIG_ID configID,
ref HTTP_SERVICE_CONFIG_SSL_QUERY pInputConfigInfo,
UInt32 InputConfigInfoLength,
IntPtr pOutputConfigInfo,
UInt32 OutputConfigInfoLength,
[In, Out] ref UInt32 pReturnLength,
IntPtr pOverlapped
);
[DllImport("httpapi.dll", CharSet = CharSet.Auto, PreserveSig = true)]
public static extern uint HttpSetServiceConfiguration(IntPtr ServiceHandle, int ConfigId, ref HTTP_SERVICE_CONFIG_SSL_SET pConfigInformation, int ConfigInformationLength, IntPtr pOverlapped);
[DllImport("httpapi.dll", CharSet = CharSet.Auto, PreserveSig = true)]
public static extern uint HttpTerminate(uint flags, IntPtr pReserved);
public static HTTP_SERVICE_CONFIG_SSL_SET MarshalConfigSslSet(IntPtr ptr) {
return (HTTP_SERVICE_CONFIG_SSL_SET)Marshal.PtrToStructure(ptr, typeof(HTTP_SERVICE_CONFIG_SSL_SET));
}
}
public enum HTTP_SERVICE_CONFIG_ID {
HttpServiceConfigIPListenList,
HttpServiceConfigSSLCertInfo,
HttpServiceConfigUrlAclInfo,
HttpServiceConfigMax
}
public enum HTTP_SERVICE_CONFIG_QUERY_TYPE {
HttpServiceConfigQueryExact,
HttpServiceConfigQueryNext,
HttpServiceConfigQueryMax
}
[StructLayout(LayoutKind.Sequential)]
public struct HTTPAPI_VERSION {
public ushort HttpApiMajorVersion;
public ushort HttpApiMinorVersion;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct HTTP_SERVICE_CONFIG_SSL_KEY {
public IntPtr pIpPort;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct HTTP_SERVICE_CONFIG_SSL_QUERY {
public HTTP_SERVICE_CONFIG_QUERY_TYPE QueryDesc;
public IntPtr KeyDesc;
public Int32 dwToken;
}
[StructLayout(LayoutKind.Sequential)]
public struct HTTP_SERVICE_CONFIG_SSL_SET {
public IntPtr KeyDesc;
public uint SslHashLength;
public IntPtr pSslHash;
public Guid AppId;
[MarshalAs(UnmanagedType.LPWStr)]
public string pSslCertStoreName;
public int DefaultCertCheckMode;
public int DefaultRevocationFreshnessTime;
public int DefaultRecovationUrlRetrievalTimeout;
[MarshalAs(UnmanagedType.LPWStr)]
public string pDefaultSslCtlIdentifier;
[MarshalAs(UnmanagedType.LPWStr)]
public string pDefaultSslCtlStoreName;
public int DefaultFlags;
}
}
'
$SUCCESS = 0
function InitializeInterop() {
try {
[Microsoft.IIS.Administration.Setup.Http] | Out-Null
}
catch {
Add-Type $cs
}
}
function GetIpEndpointBytes($_ipEndpoint) {
$socketAddress = $_ipEndpoint.Serialize()
$ipBytes = [System.Array]::CreateInstance([System.Byte], $socketAddress.Size)
for ($i = 0; $i -lt $socketAddress.Size; $i++) {
$ipBytes[$i] = $socketAddress[$i]
}
return $ipBytes
}
function GetBindingInfo($sslConfig) {
$hash = [System.Array]::CreateInstance([System.Byte], [int]($sslConfig.SslHashLength))
[System.Runtime.InteropServices.Marshal]::Copy($sslConfig.pSslHash, $hash, 0, $sslConfig.SslHashLength)
$socketAddressLength = 16
$sa = [System.Array]::CreateInstance([System.Byte], $socketAddressLength)
[System.Runtime.InteropServices.Marshal]::Copy($sslConfig.KeyDesc, $sa, 0, $socketAddressLength)
$socketAddress = New-Object "System.Net.SocketAddress" -ArgumentList ([System.Net.Sockets.AddressFamily]::InterNetwork, $socketAddressLength)
for ($i = 0; $i -lt $sa.Length; $i++) {
$socketAddress[$i] = $sa[$i]
}
$ep = New-Object "System.Net.IPEndPoint" -ArgumentList ([ipaddress]::Any, 0)
$endpoint = [System.Net.IPEndPoint]$ep.Create($socketAddress)
$ret = @{}
$ret.CertificateHash = [System.BitConverter]::ToString($hash).Replace("-", "")
$ret.AppId = $sslConfig.AppId
$ret.IpEndpoint = $endpoint
return $ret
}
function InitializeHttpSys() {
$v = New-Object "Microsoft.IIS.Administration.Setup.HTTPAPI_VERSION"
$V.HttpApiMajorVersion = 1
$v.HttpApiMinorVersion = 0
$result = [Microsoft.IIS.Administration.Setup.Http]::HttpInitialize($v, [Microsoft.IIS.Administration.Setup.Http]::HTTP_INITIALIZE_CONFIG, [System.IntPtr]::Zero)
if ($result -ne $SUCCESS) {
Write-Warning "Error initializing Http API"
throw [System.ComponentModel.Win32Exception] $([System.int32]$result)
}
return $result
}
function TerminateHttpSys() {
return [Microsoft.IIS.Administration.Setup.Http]::HttpTerminate([Microsoft.IIS.Administration.Setup.Http]::HTTP_INITIALIZE_CONFIG, [System.IntPtr]::Zero)
}
function Add-SslBinding($_ipEndpoint, $_certificate, $_appId) {
if ($_ipEndpoint -eq $null) {
throw "Ip Endpoint required."
}
if ($_certificate -eq $null) {
throw "Certificate required."
}
if ($appId -eq $null) {
throw "App id required."
}
<# FYI, [System.Guid]::Parse() is not supported in lower version of powershell
if (-not($_appId -is [System.Guid])) {
$_appId = [System.Guid]::Parse($_appId)
}
#>
setSslConfiguration $_ipEndpoint $_certificate $_appId
}
function Delete-SslBinding($_ipEndpoint) {
if ($_ipEndpoint -eq $null) {
throw "Ip Endpoint required."
}
setSslConfiguration $_ipEndpoint $null $([System.Guid]::Empty)
}
function Get-SslBinding($_ipEndpoint) {
if ($_ipEndpoint -eq $null) {
throw "Ip Endpoint required."
}
$bufferSize = 4096
try {
InitializeHttpSys| Out-Null
$ipBytes = [System.Byte[]]$(GetIpEndpointBytes($_ipEndpoint))
$hIp = [System.Runtime.InteropServices.GCHandle]::Alloc($ipBytes, [System.Runtime.InteropServices.GCHandleType]::Pinned)
$pIp = $hIp.AddrOfPinnedObject()
$queryParam = New-Object "Microsoft.IIS.Administration.Setup.HTTP_SERVICE_CONFIG_SSL_QUERY"
$queryParam.QueryDesc = [Microsoft.IIS.Administration.Setup.HTTP_SERVICE_CONFIG_QUERY_TYPE]::HttpServiceConfigQueryExact
$queryParam.dwToken = 0
$queryParam.KeyDesc = $pIp
$returnLen = 0
$pReturnSet = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($bufferSize)
$result = [Microsoft.IIS.Administration.Setup.Http]::HttpQueryServiceConfiguration(
[System.IntPtr]::Zero,
[Microsoft.IIS.Administration.Setup.HTTP_SERVICE_CONFIG_ID]::HttpServiceConfigSSLCertInfo,
[ref] $queryParam,
[uint32]([System.Runtime.InteropServices.Marshal]::SizeOf($queryParam)),
$pReturnSet,
$bufferSize,
[ref] $returnLen,
[System.IntPtr]::Zero)
if ($result -eq 2) {
# File not found
return $null
}
if ($result -ne $SUCCESS) {
Write-Warning "Error reading Ssl Cert Configuration"
throw [System.ComponentModel.Win32Exception] $([System.int32]$result)
}
$sslConfig = [Microsoft.IIS.Administration.Setup.Http]::MarshalConfigSslSet($pReturnSet)
return GetBindingInfo $sslConfig
}
finally {
if ($hIp -ne $null) {
$hIp.Free()
$hIp = $null
}
if ($pReturnSet -ne [System.IntPtr]::Zero) {
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($pReturnSet)
$pReturnSet = [System.IntPtr]::Zero
}
TerminateHttpSys | Out-Null
}
}
function setSslConfiguration($_ipEndpoint, $_certificate, $_appId) {
try {
InitializeHttpSys| Out-Null
$sslSet = New-Object "Microsoft.IIS.Administration.Setup.HTTP_SERVICE_CONFIG_SSL_SET"
$sslSetSize = [System.Runtime.InteropServices.Marshal]::SizeOf($sslSet)
$ipBytes = [System.Byte[]]$(GetIpEndpointBytes($_ipEndpoint))
$hIp = [System.Runtime.InteropServices.GCHandle]::Alloc($ipBytes, [System.Runtime.InteropServices.GCHandleType]::Pinned)
$pIp = $hIp.AddrOfPinnedObject()
$sslSet.KeyDesc = $pIp # IntPtr
$sslSet.SslHashLength = 0
$sslSet.pSslHash = [System.IntPtr]::Zero
$sslSet.pSslCertStoreName = [System.IntPtr]::Zero
$sslSet.AppId = $_appId
if ($_certificate -ne $null) {
# Create binding
$certBytes = $_certificate.GetCertHash()
$hCertBytes = [System.Runtime.InteropServices.GCHandle]::Alloc($certBytes, [System.Runtime.InteropServices.GCHandleType]::Pinned)
$pCertBytes = $hCertBytes.AddrOfPinnedObject()
$sslSet.SslHashLength = 20
$sslSet.pSslHash = $pCertBytes
$sslSet.pSslCertStoreName = $StoreName
$result = [Microsoft.IIS.Administration.Setup.Http]::HttpSetServiceConfiguration([System.IntPtr]::Zero,
[Microsoft.IIS.Administration.Setup.Http]::HTTP_SERVICE_CONFIG_SSLCERT_INFO,
[ref]$sslSet,
$sslSetSize,
[System.IntPtr]::Zero)
}
else {
#Delete binding
$result = [Microsoft.IIS.Administration.Setup.Http]::HttpDeleteServiceConfiguration([System.IntPtr]::Zero,
[Microsoft.IIS.Administration.Setup.Http]::HTTP_SERVICE_CONFIG_SSLCERT_INFO,
[ref]$sslSet,
$sslSetSize,
[System.IntPtr]::Zero)
}
if ($result -ne $SUCCESS) {
Write-Warning "Error setting Ssl Cert Configuration"
throw [System.ComponentModel.Win32Exception] $([System.int32]$result)
}
}
finally {
if ($hIp -ne $null) {
$hIp.Free()
$hIp = $null
}
if ($hCertBytes -ne $null) {
$hCertBytes.Free()
$hCertBytes = $null
}
TerminateHttpSys| Out-Null
}
}
InitializeInterop
switch ($Command)
{
"Add-SslBinding"
{
return Add-SslBinding $IpEndPoint $Certificate $AppId
}
"Delete-SslBinding"
{
return Delete-SslBinding $IpEndpoint
}
"Get-SslBinding"
{
return Get-SslBinding $IpEndpoint
}
default
{
throw "Unknown command"
}
}