diff --git a/ClrHttpRequest/bin/Debug/ClrHttpRequest.dacpac b/ClrHttpRequest/bin/Debug/ClrHttpRequest.dacpac
index d4991c8..725a12c 100644
Binary files a/ClrHttpRequest/bin/Debug/ClrHttpRequest.dacpac and b/ClrHttpRequest/bin/Debug/ClrHttpRequest.dacpac differ
diff --git a/ClrHttpRequest/bin/Debug/ClrHttpRequest.dll b/ClrHttpRequest/bin/Debug/ClrHttpRequest.dll
index da0c6e4..bbed238 100644
Binary files a/ClrHttpRequest/bin/Debug/ClrHttpRequest.dll and b/ClrHttpRequest/bin/Debug/ClrHttpRequest.dll differ
diff --git a/ClrHttpRequest/bin/Debug/ClrHttpRequest.pdb b/ClrHttpRequest/bin/Debug/ClrHttpRequest.pdb
index 071ad1b..28b1543 100644
Binary files a/ClrHttpRequest/bin/Debug/ClrHttpRequest.pdb and b/ClrHttpRequest/bin/Debug/ClrHttpRequest.pdb differ
diff --git a/ClrHttpRequest/clr_http_request.cs b/ClrHttpRequest/clr_http_request.cs
index fc7bc1c..3d7636f 100644
--- a/ClrHttpRequest/clr_http_request.cs
+++ b/ClrHttpRequest/clr_http_request.cs
@@ -3,6 +3,7 @@
using System.Data.SqlTypes;
using System.IO;
using System.Net;
+using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml.Linq;
@@ -138,6 +139,12 @@ public static SqlXml clr_http_request(string requestMethod, string url, string p
}
debugXml.Add(GetDebugStepXElement("Processed Headers"));
+ if (options.ContainsKey("client_certificate_name"))
+ {
+ var clientCertName = options["client_certificate_name"];
+ request.ClientCertificates.Add(ReadClientCertificate(clientCertName, debugXml));
+ }
+
// Set the timeout if provided as an option
if (options.ContainsKey("timeout"))
{
@@ -156,11 +163,12 @@ public static SqlXml clr_http_request(string requestMethod, string url, string p
// Add in non-GET parameters provided
if (requestMethod.ToUpper() != "GET" && !string.IsNullOrWhiteSpace(parameters))
{
- // Convert to byte array
- var parameterData = Encoding.ASCII.GetBytes(parameters);
-
- // Set content info
- if (!contentLengthSetFromHeaders)
+ var encoding = GetEncoding(options, debugXml);
+ // Convert to byte array
+ var parameterData = encoding.GetBytes(parameters);
+
+ // Set content info
+ if (!contentLengthSetFromHeaders)
{
request.ContentLength = parameterData.Length;
}
@@ -304,11 +312,81 @@ public static SqlXml clr_http_request(string requestMethod, string url, string p
}
///
- /// Generates an XML element with a standard format for debug steps
+ /// Tries to get encoding from options
///
- /// Name for this step in the process
- /// XElement representing a debug step
- private static XElement GetDebugStepXElement(string stepName)
+ private static Encoding GetEncoding(Dictionary options, XElement debugXml)
+ {
+ var encodingKey = "encoding";
+ Encoding result = Encoding.ASCII;
+ try
+ {
+ if (options.ContainsKey(encodingKey))
+ {
+ result = Encoding.GetEncoding(options[encodingKey]);
+ }
+ }
+ catch (Exception ex)
+ {
+ debugXml.Add(GetDebugStepXElement($"Error while parse specified encoding value {options[encodingKey]}"));
+
+ debugXml.Add(GetXElementFromException(ex));
+ }
+
+ debugXml.Add(GetDebugStepXElement($"Set encoding to {result}"));
+
+ return result;
+ }
+
+ ///
+ /// Tries to read certificate by its name from certificates store
+ ///
+ /// CN or Subject Distinguished name
+ /// Client certificate
+ private static X509Certificate2 ReadClientCertificate(string clientCertName, XElement debugXml)
+ {
+ var storeLocations = new StoreLocation[] { StoreLocation.LocalMachine, StoreLocation.CurrentUser };
+ var storeNames = new StoreName[] { StoreName.Root, StoreName.My, StoreName.TrustedPeople, StoreName.AuthRoot };
+
+ foreach (var storeLocation in storeLocations)
+ foreach (var storeName in storeNames)
+ {
+ X509Store store = null;
+ try
+ {
+ store = new X509Store(storeName, storeLocation);
+ store.Open(OpenFlags.ReadOnly);
+
+ foreach (var cert in store.Certificates)
+ {
+ if (cert.SubjectName?.Name?.Contains(clientCertName) == true
+ || cert.Subject?.Contains(clientCertName) == true)
+ {
+ debugXml.Add(GetDebugStepXElement($"Client certificate founded: {cert.Subject ?? cert.SubjectName?.Name}"));
+ return cert;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ debugXml.Add(GetDebugStepXElement($"Error while reading store {storeName} for {storeLocation}"));
+
+ debugXml.Add(GetXElementFromException(ex));
+ }
+ finally
+ {
+ store?.Close();
+ }
+ }
+
+ throw new Exception($"Cannot find any certificate by name {clientCertName}");
+ }
+
+ ///
+ /// Generates an XML element with a standard format for debug steps
+ ///
+ /// Name for this step in the process
+ /// XElement representing a debug step
+ private static XElement GetDebugStepXElement(string stepName)
{
return new XElement(
"Step",
diff --git a/README.md b/README.md
index 1180301..5b49fae 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,18 @@ If you're waiting for me or have any questions for me, bug me!
Pass a CSV of protocols from the [SecurityProtocolType Enum](https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype)
Example: `Tls12,Tls11,Tls`
+
+- encoding
+
+ Encoding of http request content. Default is ASCII
+
+ Example: `utf-8`
+
+- client_certificate_name
+
+ For mTLS support, pass a client certificate name to locate it in the server's certificate store by Subject or Subject Name and use it in the HTTP request.
+
+ Example: `Cert's Subject Name`
- timeout