Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show X.509 extensions in certificate output #831

Merged
merged 5 commits into from
Jun 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/dev/impl/DevToys/Helpers/CertificateHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using DevToys.Core;

namespace DevToys.Helpers
{
Expand Down Expand Up @@ -47,9 +48,10 @@ internal static bool TryDecodeCertificate(string input, string? password, out st
}
}

X509Certificate2 certificate;
try
{
var certificate = new X509Certificate2(Convert.FromBase64String(publicCert), password);
certificate = new X509Certificate2(Convert.FromBase64String(publicCert), password);
decoded = certificate.ToString();
}
catch (CryptographicException wce)
Expand All @@ -68,9 +70,44 @@ internal static bool TryDecodeCertificate(string input, string? password, out st
return false;
}

if (!string.IsNullOrEmpty(decoded) && certificate.Extensions.OfType<X509Extension>().Any())
{
decoded = string.Join(Environment.NewLine, decoded, DecodeExtensions(certificate));
}

return true;
}

private static string DecodeExtensions(X509Certificate2 certificate)
{
// Try to decode the X.509 extensions.
try
{
StringBuilder extensionData = new();
foreach (X509Extension x509Extension in certificate.Extensions)
{
AsnEncodedData asnEncodedData = new(x509Extension.Oid, x509Extension.RawData);

// Add the name in brackets to match the previous output from X509Certificate.ToString()
extensionData.AppendLine($"[{x509Extension.Oid.FriendlyName}]");

// Add each line of the data, indented by two spaces to match the output from X509Certificate.ToString()
foreach (string dataLine in asnEncodedData.Format(multiLine: true).Split(Environment.NewLine))
{
extensionData.AppendLine($" {dataLine}");
}
}

return extensionData.ToString().Trim();
}
catch (Exception ex)
{
Logger.LogFault("Failed to parse X.509 extensions from certificate.", ex);
}

return string.Empty;
}

/// <summary>
/// Get the string data from a certificate file.
/// If the string is pem format with plain text values, it will return as is.
Expand Down
188 changes: 179 additions & 9 deletions src/tests/DevToys.Tests/Helpers/CertificateHelperTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Text;
using DevToys.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand All @@ -12,6 +13,9 @@ public class CertificateHelperTests
[DataRow(PemCertWithPrivateKey, null, true, CertDecoded)]
[DataRow(PfxNoPassword, null, true, CertDecoded)]
[DataRow(PfxWithPassword, "test1234", true, CertDecoded)]
[DataRow(PemCertPublicWithExtensions, null, true, CertWithExtensionsDecoded)]
[DataRow(PemCertWithPrivateKeyWithExtensions, null, true, CertWithExtensionsDecoded)]
[DataRow(PfxWithExtensionsNoPassword, null, true, CertWithExtensionsDecoded)]
public void DecodeCertificateSuccess(string input, string password, bool successfullyDecoded, string expectedResult)
=> DecodeCertificate(input, password, successfullyDecoded, expectedResult);

Expand All @@ -25,20 +29,34 @@ public void DecodeCertificateErrors()

private void DecodeCertificate(string input, string password, bool successfullyDecoded, string expectedResult)
{
// Convert date output to UTC so that tests pass on any machine with any timezone
var notBefore = DateTime.Parse("4/17/2023 10:40:48 AM");
var notAfter = DateTime.Parse("4/16/2024 10:40:48 AM");
var easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime notBeforeUtc = TimeZoneInfo.ConvertTimeToUtc(notBefore, easternZone);
DateTime notAfterUtc = TimeZoneInfo.ConvertTimeToUtc(notAfter, easternZone);

bool result = CertificateHelper.TryDecodeCertificate(input, password, out string decoded);
decoded = decoded.Replace(notBefore.ToString(), notBeforeUtc.ToString());
decoded = decoded.Replace(notAfter.ToString(), notAfterUtc.ToString());
decoded = CleanDateTimes(decoded);
expectedResult = CleanDateTimes(expectedResult);
Assert.AreEqual(result, successfullyDecoded);
Assert.AreEqual(expectedResult, decoded);
}

/// <summary>
/// Strips times from decoded certificates to avoid issues with testing in different timezones
/// </summary>
private string CleanDateTimes(string decoded)
{
var decodedCleaned = new StringBuilder();
foreach (string line in decoded.Split(Environment.NewLine))
{
if (DateTime.TryParse(line, out DateTime dateTime))
{
decodedCleaned.AppendLine($" {dateTime.Date}");
}
else
{
decodedCleaned.AppendLine(line);
}
}

return decodedCleaned.ToString();
}

/// <summary>
/// These certificates can be created via
/// PEM:
Expand Down Expand Up @@ -210,5 +228,157 @@ [Not After]
[Thumbprint]
9E5B55F43B571EFE82D386C856506FC96018217C
";

/// <summary>
/// PEM: openssl req -x509 -newkey rsa:4096 -keyout test-devtoys.key -out test-devtoys.pem -sha256 -days 3650 -nodes
/// PFX: openssl pkcs12 -inkey test-devtoys.key -in test-devtoys.pem -export -out test-devtoys.pfx | base64 -w 0
/// </summary>
private const string PemCertPublicWithExtensions = @"-----BEGIN CERTIFICATE-----
MIIFSzCCAzOgAwIBAgIUCi1nl4PF23ETmg7rNHnU99XHbnkwDQYJKoZIhvcNAQEL
BQAwNTELMAkGA1UEBhMCRFQxEzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAoM
CERldi1Ub3lzMB4XDTIzMDYwMjEzMTIwM1oXDTMzMDUzMDEzMTIwM1owNTELMAkG
A1UEBhMCRFQxEzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAoMCERldi1Ub3lz
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuxTovybg74QlGSo7lGlX
Kxz7nq0Mfvulj+t2NVfGXEu6VUTaDZNDYwynRR397N82RKsYt17dSkjUYWZI5nsu
QfH9LJIlstokqu+6/CXA5T79TGNYVz9iFbMQXNy+M2zLaO42LCOHUs8oYFB5FXL7
x6nXmtsJUB0eITPc2wrKCN7SY3QuMZ/A6Bn2dVJ+7+2SwCRLv7SDDGX/jxiLCxXn
tdmgv/PKQnxq0JXIi3n1BOrdz6e9DYDMjPR5FkZWh0TEjFMpCSxqq/IfQc/DNYii
ragXuvoVpLHswRu4C8ytksZYEg+RjozqjTpVI4c++xXQpNK4zIs0bI6bkczj4T97
WEzE3xE90RD7gG09tEL7/dKeok5o0zC6N+1rNTPMKTwDPEUig8uNcplN7W5vWehi
ixJGceIFb91zMr+W1ImY39cFllQZnQrBvfsoq0IxHaadYjdvA/C2H1FsFPT4SgSH
D/9HgWyu1wAt7e/xcHa9OM71ac1SpXN2br0Up0P4STXPeolf5I0BsAffNdRrKRM0
cSZpftnM/gFjoJVcd2unVA4suFoXArHGSRKdqPb3XjnqcFU7MjHDycRXzhfUj90a
Th9pg6lovY1gq9TS8vfNC6zd/W2GcxDD2+ONjsGBnfmF4BLo9eVeMu3kvtMG9l09
JZZKqQc0x9IxwTZx1JP2CVUCAwEAAaNTMFEwHQYDVR0OBBYEFCad/q2ghyzGQ1B5
LCVY32+ghsQbMB8GA1UdIwQYMBaAFCad/q2ghyzGQ1B5LCVY32+ghsQbMA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAG+/mgWf0zhVXcbZKgrvBqsM
a5qiJPUp4JSjUtUs5TGQRM5HICS+flgzhUa55xDjy9q5Lv3FI04KwT6K8XaSGBGr
zkp2Vyxn5DDboYK2FiJEHJojGzqElOELdOSbMYLO8bLY7SK3eH8pMuy6jPOqABOp
hpVAWhrhvDoBsREwXZDD1CrlorfBCM5ue2Xa1elH3qswG3HkKWq+Ieis8AlFvx7i
JPzw5KTGBdAZzsakF1KN9qC9Xjzoc526yo0RvlsOjK0i9XKpWkjE2pAE/jpqcapZ
Y+mgKJe9wl+ZfZ1Z2AEk9U/T4aZMmxsqo5z3ztjuB290oxR8dnfJlZobAOyFnA3G
5y1/s1Sw0hmKpY7563/c+vvWBwq48R1hYOLB5UsNBSijD+KyAii+ATd0yp/ItPb0
Ng5ek4rln1BaCumKTJ4WAMBDtqBFCVd0xNidRoI1Ljug8+MPafVzw8KIYP1FrnOy
Vx0sorzO7j2T2rLtTO/RrCbVErBHHyHvLLjxeKnVdc1khtBG/MQv6zkb2w8oxl/9
6BmGhbw4S5Ti33qO4r1bs8if1zzZJgckcf7f7DPzNvBslUlARy6Kz3CRc2sK9lwm
JIff9Js+BoWwO3KGDnk8paJf/YNBc1qaiFQTHOBCOefqOaTiHG5TGSklO7Ij+xNx
ulEjWxGlNtL7tLMeToVU
-----END CERTIFICATE-----";

private const string PemCertWithPrivateKeyWithExtensions = @"-----BEGIN PRIVATE KEY-----
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
6WNJmQTHY+2bURcR9d1vIO3eGkCrGvs=
-----END PRIVATE KEY-----

-----BEGIN CERTIFICATE-----
MIIFSzCCAzOgAwIBAgIUCi1nl4PF23ETmg7rNHnU99XHbnkwDQYJKoZIhvcNAQEL
BQAwNTELMAkGA1UEBhMCRFQxEzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAoM
CERldi1Ub3lzMB4XDTIzMDYwMjEzMTIwM1oXDTMzMDUzMDEzMTIwM1owNTELMAkG
A1UEBhMCRFQxEzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAoMCERldi1Ub3lz
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuxTovybg74QlGSo7lGlX
Kxz7nq0Mfvulj+t2NVfGXEu6VUTaDZNDYwynRR397N82RKsYt17dSkjUYWZI5nsu
QfH9LJIlstokqu+6/CXA5T79TGNYVz9iFbMQXNy+M2zLaO42LCOHUs8oYFB5FXL7
x6nXmtsJUB0eITPc2wrKCN7SY3QuMZ/A6Bn2dVJ+7+2SwCRLv7SDDGX/jxiLCxXn
tdmgv/PKQnxq0JXIi3n1BOrdz6e9DYDMjPR5FkZWh0TEjFMpCSxqq/IfQc/DNYii
ragXuvoVpLHswRu4C8ytksZYEg+RjozqjTpVI4c++xXQpNK4zIs0bI6bkczj4T97
WEzE3xE90RD7gG09tEL7/dKeok5o0zC6N+1rNTPMKTwDPEUig8uNcplN7W5vWehi
ixJGceIFb91zMr+W1ImY39cFllQZnQrBvfsoq0IxHaadYjdvA/C2H1FsFPT4SgSH
D/9HgWyu1wAt7e/xcHa9OM71ac1SpXN2br0Up0P4STXPeolf5I0BsAffNdRrKRM0
cSZpftnM/gFjoJVcd2unVA4suFoXArHGSRKdqPb3XjnqcFU7MjHDycRXzhfUj90a
Th9pg6lovY1gq9TS8vfNC6zd/W2GcxDD2+ONjsGBnfmF4BLo9eVeMu3kvtMG9l09
JZZKqQc0x9IxwTZx1JP2CVUCAwEAAaNTMFEwHQYDVR0OBBYEFCad/q2ghyzGQ1B5
LCVY32+ghsQbMB8GA1UdIwQYMBaAFCad/q2ghyzGQ1B5LCVY32+ghsQbMA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAG+/mgWf0zhVXcbZKgrvBqsM
a5qiJPUp4JSjUtUs5TGQRM5HICS+flgzhUa55xDjy9q5Lv3FI04KwT6K8XaSGBGr
zkp2Vyxn5DDboYK2FiJEHJojGzqElOELdOSbMYLO8bLY7SK3eH8pMuy6jPOqABOp
hpVAWhrhvDoBsREwXZDD1CrlorfBCM5ue2Xa1elH3qswG3HkKWq+Ieis8AlFvx7i
JPzw5KTGBdAZzsakF1KN9qC9Xjzoc526yo0RvlsOjK0i9XKpWkjE2pAE/jpqcapZ
Y+mgKJe9wl+ZfZ1Z2AEk9U/T4aZMmxsqo5z3ztjuB290oxR8dnfJlZobAOyFnA3G
5y1/s1Sw0hmKpY7563/c+vvWBwq48R1hYOLB5UsNBSijD+KyAii+ATd0yp/ItPb0
Ng5ek4rln1BaCumKTJ4WAMBDtqBFCVd0xNidRoI1Ljug8+MPafVzw8KIYP1FrnOy
Vx0sorzO7j2T2rLtTO/RrCbVErBHHyHvLLjxeKnVdc1khtBG/MQv6zkb2w8oxl/9
6BmGhbw4S5Ti33qO4r1bs8if1zzZJgckcf7f7DPzNvBslUlARy6Kz3CRc2sK9lwm
JIff9Js+BoWwO3KGDnk8paJf/YNBc1qaiFQTHOBCOefqOaTiHG5TGSklO7Ij+xNx
ulEjWxGlNtL7tLMeToVU
-----END CERTIFICATE-----
";

private const string PfxWithExtensionsNoPassword = "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";

private const string CertWithExtensionsDecoded = @"[Subject]
O=Dev-Toys, S=Some-State, C=DT

[Issuer]
O=Dev-Toys, S=Some-State, C=DT

[Serial Number]
0A2D679783C5DB71139A0EEB3479D4F7D5C76E79

[Not Before]
6/2/2023 9:12:03 AM

[Not After]
5/30/2033 9:12:03 AM

[Thumbprint]
41D8D90FE633CFF63CED9E4A53889C0133AEF106

[Subject Key Identifier]
269dfeada0872cc64350792c2558df6fa086c41b

[Authority Key Identifier]
KeyID=269dfeada0872cc64350792c2558df6fa086c41b

[Basic Constraints]
Subject Type=CA
Path Length Constraint=None";
}
}