Skip to content

Instantly share code, notes, and snippets.

@jtgasper3
Created September 6, 2023 20:58
Show Gist options
  • Save jtgasper3/955578ec7453c23fa8d7e38f8dc223ac to your computer and use it in GitHub Desktop.
Save jtgasper3/955578ec7453c23fa8d7e38f8dc223ac to your computer and use it in GitHub Desktop.
Azure Function for MDM Client Cert validation
#r "Newtonsoft.Json"
#r "System.Formats.Asn1"
using System.Formats.Asn1;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
static X509Certificate2 interCertificate = new X509Certificate2(Convert.FromBase64String(
@"MIIEfTCCAmWgAwIBAgIQQt4O1ZNyWaBPqw1mUq1F8jANBgkqhkiG9w0BAQsFADA4
MTYwNAYDVQQDEy1NaWNyb3NvZnQgSW50dW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwHhcNMjMwNTA1MDAwMDAwWhcNMjYwNTA1MDAwMDAwWjApMScwJQYD
VQQDEx5NaWNyb3NvZnQgSW50dW5lIE1ETSBEZXZpY2UgQ0EwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQDYOy/IV8f+BmH5y9/zJUv5oROn3MEQua39r03K
7fgAqwEsAFtU/4ZWnFbriFSx0xZsDkZxxUy9BlOQZ+2sr/eJ2j6uI6CocR4woRkt
JR6pQ5EXdsJb9lZOfV9+q6PwLnAIBQTTpUq+XtE8T1MwwGQ5NWW1JqQMDPo3nR0T
h14tDQNrLtCPe7c2f+qaG2x+uRvWGMgTmxtGpYiipQB8upnInAZZ5wVdhu+/GMZg
TSYJvn+3Px33NOB+0mpVanq7CwzyXA+dqGio8sZJDfOS1fPYDKqPj52l3rct3doa
2o98gPHWaDs7Rj+GiAKeidUkN3uhQUyNfi8+YJEHb8OVGjEFAgMBAAGjgZEwgY4w
HQYDVR0OBBYEFC5aTr/lv8IEGDqYYmHaaj3AsHw4MB8GA1UdIwQYMBaAFCtJzrEH
JUyd5gqT85C5MPfmRJ+SMBAGCSsGAQQBgjcVAQQDAgEAMA4GA1UdDwEB/wQEAwIB
hjAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMA0G
CSqGSIb3DQEBCwUAA4ICAQCoci1EyidjS6xqi3S37/TJdIGqM0OcvTsi12WPIVxA
MFRaF638dgu9FrRpTDCquc06lBHtIfBWCU7VFkGIGGIZ54L4en/dGi0ZBXjv4vfp
RQ+LsoJZnYtCF3MbvlPXfQr3nk1k6m7c1utsuSzL0VgeEJJ++5KXlMRHrFdlXXm5
N8MJNw78I/e7sbwNZxvA4UdxnEpqHeKDj1+nppANJDKrobIp9WzmYCFA8Qu3bX/N
g9OnxLnpGi8mCIvPCkPu3oo1RtHxjf/tQtdSE4V1/8mggYLNNftjWk6ElqA+gRGY
gJCssJrfG/bRYk+9ZQjavu3oG0B+SD7j3UeAOQqoOaPi8faF95tpgpVfJqQ6bm9w
qBdODWfE4TYIPd0QG5nsYr86JThEtsL36U9j74wC33+4YHV4UviUiycyQiQQCSIF
xSY2u9cLpFO4rjM+Qe/n8KWQURTW8i3wUFReVrMp9l+y70GklU/HHkXv8SDtFi6Q
0+aK2duUhbHr+8P2nPuPNetfryVsdE+RVL4pFWEfIvbOKOI5EQj3Bh4/XkbhQtZk
+FR7VkUIm5A9Kp+OzsQADkYhWE1N4xL/ysfRWmOcjB1MT1m7Zfie3YsR7j+rY4NL
b+snuHqqpYRDSZpngZAJgRYHI1xSUxOk+zJ8XyHG+eyeZdfwlrWLKuMJEFw4pczs
0Q=="));
static X509Certificate2 rootCertificate = new X509Certificate2(Convert.FromBase64String(
@"MIIFaTCCA1GgAwIBAgIQWhvaqZFB9KVNpmVRc71GWjANBgkqhkiG9w0BAQsFADA4
MTYwNAYDVQQDEy1NaWNyb3NvZnQgSW50dW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwHhcNMjEwODEyMDAwMDAwWhcNMjYwODEyMDAwMDAwWjA4MTYwNAYD
VQQDEy1NaWNyb3NvZnQgSW50dW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDQL63MwZDlW02yCipf
dAUGn5Q8sSIc6zjdTkdCRh2SZGVFVLCgFr/EULGw3q7xaOS/mTA2I+koZM+95JvE
xfsIy+S7I7rLQjYNuJZVXl0s1xj4hfewF1jk8nWjdEwYfZkbZ+/6pYTOtHg/U3tV
seT63tg6Vc94dkUpOmN4tlyuie2qbMbc1VmAyyITcoIFtRspdE9qW1NnwgzGJz/Q
KaL2H7LtGyNGwXhH4jSwZa8ZNcwKjJ7wdaG2SNxOgrZsCHv272vugNiGEy4yYwGU
CyGOAksOaHEobW87Y92s/q4/5beBa979ZCeH4VSpRo2LcQFH9ZM9r05VpFimg8Pu
pcOl2g1WcuBj1MBBjT/YPaVgbZUp9wvqqefhW6m/JGTYaPJ4YrnMnrzem6kwGP5K
3xwHWL/5ANTjWDx6+b7dZ5f1AKf2DQFaxslMk4CqTElABI8Te1QzTG/QOmSzewtV
cwcWGo4F7yJyBbnYcqvN5XJWURFjecuoTvyxPG3R2ljfTKSyJhrTkP0hw3Zxr3cn
pY0ZtK80mKE0A9pFG597Qy3q/9TJNRtDjeQrr7DCPwQ8cvfeyT568J+hUyAeN7PV
hOV/OXkWXZND9bbkYKNE4ebfiNTGCeJX9iIGjZfbzz86eDqEVpgHcJstnWN6VWbR
qkrK/p6m5fqYNZCMAN+g4OyHZQIDAQABo28wbTAdBgNVHQ4EFgQUK0nOsQclTJ3m
CpPzkLkw9+ZEn5IwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQEw
FgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI
hvcNAQELBQADggIBAGrkO1h+8wKHJHCMf/jRz5DhvigyDOyohx2LlbhL/i/0uSka
/u/m9uhi0UXXYX9Y15tpRUjZDqtQAjpnFePNcxe3/RRp5kuqVHaMyOHShjTTJ6YD
hKhlZP37qSBuWv+x2RsUrPzVdpFyljJWyJ1yruBJqgJ2sq3lskb8cO94fhcc2StT
uO9aB+0YY2ce5OHtOgj39Enx+PRCpweIodhAKdZuTVAX1M4qeBJD87Gg/7b4VfaS
aM6Frzrh9VuylCM258Xx1CYGgzTYJCcvCYOHA74nS3XigalsKonbdVHUEac+4D7i
P33JSlV1wlxYPPJqayiBam21YtSHmdJKV3pwkFbvlX2+pNioX86E48YaNz4faq3v
Cl4xHqMfVfOOG8QLiOnNlHsBKsffD420CKi2SJaKETPJnOLG61265jiT4Yr1mUeW
G+tQTquFeFdTTSGfToyXE58IMLhI19hQtf/2HU9aZK/vJsjWPYKCucPCXwQZA2Kk
Z/RT8HsSPPet3GyP3gL0nzfacohJ7RKwClE82exXgiGK/UAFqvEL9pwbLJtX4Hx/
+OhT4zQ9CSppKjSBDIRR8gV6G2HY5gWKXq9K+/Dv+m1APPWsR1kTsqy+tAQPRxlK
7bDsXt5GawcCP+FM0vJwd5O+ZPB3x5VLG5OLv48cCRUxf1alMQSfsJz/r2L4"));
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
// string name = req.Query["name"];
bool valid = false;
using (var chain = new X509Chain()) {
chain.ChainPolicy.CustomTrustStore.Add(interCertificate);
chain.ChainPolicy.CustomTrustStore.Add(rootCertificate);
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
valid = chain.Build(req.HttpContext.Connection.ClientCertificate);
if (!valid) {
foreach (X509ChainElement element in chain.ChainElements) {
log.LogInformation("Subject name: {0}", element.Certificate.Issuer);
log.LogInformation("Issuer name: {0}", element.Certificate.Issuer);
log.LogInformation("Certificate valid until: {0}", element.Certificate.NotAfter);
log.LogInformation("Certificate is valid: {0}", element.Certificate.Verify());
log.LogInformation("Error status length: {0}", element.ChainElementStatus.Length);
for (int index = 0; index < element.ChainElementStatus.Length; index++) {
log.LogInformation("- {0}: {1}: {2}", index, element.ChainElementStatus[index].Status.ToString(), element.ChainElementStatus[index].StatusInformation.ToString());
}
}
}
}
string subject = req.HttpContext.Connection.ClientCertificate.Subject;
string issuer = req.HttpContext.Connection.ClientCertificate.Issuer;
var extensions = req.HttpContext.Connection.ClientCertificate.Extensions;
var tenantIdExt = extensions.Where(x => x.Oid.Value.Equals("1.2.840.113556.5.14")).FirstOrDefault();
byte[] value = System.Formats.Asn1.AsnDecoder.ReadOctetString(tenantIdExt.RawData, System.Formats.Asn1.AsnEncodingRules.DER, out _);
string encodedTenantId = string.Join(string.Empty, Array.ConvertAll(value, b => b.ToString("X2")));
log.LogInformation(encodedTenantId); // TenantId bytes are a bit scambled, so either compare to scambled equivalent or descramble and compare to real value.
// string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
// dynamic data = JsonConvert.DeserializeObject(requestBody);
// string name = name ?? data?.name;
// string responseMessage = string.IsNullOrEmpty(name)
// ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
// : $"Hello, {subject} ({issuer}). The cert is valid: {valid}";
string responseMessage = $"Hello, {subject} ({issuer}). The cert is valid: {valid}";
return new OkObjectResult(responseMessage);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment