Content Supported by Sourcelens Consulting
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using Test.Cryptography;
using Xunit;
namespace System.Security.Cryptography.X509Certificates.Tests
{
public static class ExtensionsTests
{
[Fact]
public static void ReadExtensions()
{
using (X509Certificate2 c = new X509Certificate2(TestData.MsCertificate))
{
X509ExtensionCollection exts = c.Extensions;
int count = exts.Count;
Assert.Equal(6, count);
X509Extension[] extensions = new X509Extension[count];
exts.CopyTo(extensions, 0);
extensions = extensions.OrderBy(e => e.Oid.Value).ToArray();
// There are an awful lot of magic-looking values in this large test.
// These values are embedded within the certificate, and the test is
// just verifying the object interpretation. In the event the test data
// (TestData.MsCertificate) is replaced, this whole body will need to be
// redone.
{
// Authority Information Access
X509Extension aia = extensions[0];
Assert.Equal("1.3.6.1.5.5.7.1.1", aia.Oid.Value);
Assert.False(aia.Critical);
byte[] expectedDer = (
"304c304a06082b06010505073002863e687474703a2f2f7777772e6d" +
"6963726f736f66742e636f6d2f706b692f63657274732f4d6963436f" +
"645369675043415f30382d33312d323031302e637274").HexToByteArray();
Assert.Equal(expectedDer, aia.RawData);
}
{
// Subject Key Identifier
X509Extension skid = extensions[1];
Assert.Equal("2.5.29.14", skid.Oid.Value);
Assert.False(skid.Critical);
byte[] expected = "04145971a65a334dda980780ff841ebe87f9723241f2".HexToByteArray();
Assert.Equal(expected, skid.RawData);
Assert.True(skid is X509SubjectKeyIdentifierExtension);
X509SubjectKeyIdentifierExtension rich = (X509SubjectKeyIdentifierExtension)skid;
Assert.Equal("5971A65A334DDA980780FF841EBE87F9723241F2", rich.SubjectKeyIdentifier);
}
{
// Subject Alternative Names
X509Extension sans = extensions[2];
Assert.Equal("2.5.29.17", sans.Oid.Value);
Assert.False(sans.Critical);
byte[] expected = (
"3048a4463044310d300b060355040b13044d4f505231333031060355" +
"0405132a33313539352b34666166306237312d616433372d34616133" +
"2d613637312d373662633035323334346164").HexToByteArray();
Assert.Equal(expected, sans.RawData);
}
{
// CRL Distribution Points
X509Extension cdps = extensions[3];
Assert.Equal("2.5.29.31", cdps.Oid.Value);
Assert.False(cdps.Critical);
byte[] expected = (
"304d304ba049a0478645687474703a2f2f63726c2e6d6963726f736f" +
"66742e636f6d2f706b692f63726c2f70726f64756374732f4d696343" +
"6f645369675043415f30382d33312d323031302e63726c").HexToByteArray();
Assert.Equal(expected, cdps.RawData);
}
{
// Authority Key Identifier
X509Extension akid = extensions[4];
Assert.Equal("2.5.29.35", akid.Oid.Value);
Assert.False(akid.Critical);
byte[] expected = "30168014cb11e8cad2b4165801c9372e331616b94c9a0a1f".HexToByteArray();
Assert.Equal(expected, akid.RawData);
}
{
// Extended Key Usage (X.509/2000 says Extended, Win32/NetFX say Enhanced)
X509Extension eku = extensions[5];
Assert.Equal("2.5.29.37", eku.Oid.Value);
Assert.False(eku.Critical);
byte[] expected = "300a06082b06010505070303".HexToByteArray();
Assert.Equal(expected, eku.RawData);
Assert.True(eku is X509EnhancedKeyUsageExtension);
X509EnhancedKeyUsageExtension rich = (X509EnhancedKeyUsageExtension)eku;
OidCollection usages = rich.EnhancedKeyUsages;
Assert.Equal(1, usages.Count);
Oid oid = usages[0];
// Code Signing
Assert.Equal("1.3.6.1.5.5.7.3.3", oid.Value);
}
}
}
[Fact]
public static void KeyUsageExtensionDefaultCtor()
{
X509KeyUsageExtension e = new X509KeyUsageExtension();
string oidValue = e.Oid.Value;
Assert.Equal("2.5.29.15", oidValue);
byte[] r = e.RawData;
Assert.Null(r);
X509KeyUsageFlags keyUsages = e.KeyUsages;
Assert.Equal(X509KeyUsageFlags.None, keyUsages);
}
[Fact]
public static void KeyUsageExtension_CrlSign()
{
TestKeyUsageExtension(X509KeyUsageFlags.CrlSign, false, "03020102".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_DataEncipherment()
{
TestKeyUsageExtension(X509KeyUsageFlags.DataEncipherment, false, "03020410".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_DecipherOnly()
{
TestKeyUsageExtension(X509KeyUsageFlags.DecipherOnly, false, "0303070080".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_DigitalSignature()
{
TestKeyUsageExtension(X509KeyUsageFlags.DigitalSignature, false, "03020780".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_EncipherOnly()
{
TestKeyUsageExtension(X509KeyUsageFlags.EncipherOnly, false, "03020001".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_KeyAgreement()
{
TestKeyUsageExtension(X509KeyUsageFlags.KeyAgreement, false, "03020308".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_KeyCertSign()
{
TestKeyUsageExtension(X509KeyUsageFlags.KeyCertSign, false, "03020204".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_KeyEncipherment()
{
TestKeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, false, "03020520".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_None()
{
TestKeyUsageExtension(X509KeyUsageFlags.None, false, "030100".HexToByteArray());
}
[Fact]
public static void KeyUsageExtension_NonRepudiation()
{
TestKeyUsageExtension(X509KeyUsageFlags.NonRepudiation, false, "03020640".HexToByteArray());
}
[Fact]
public static void BasicConstraintsExtensionDefault()
{
X509BasicConstraintsExtension e = new X509BasicConstraintsExtension();
string oidValue = e.Oid.Value;
Assert.Equal("2.5.29.19", oidValue);
byte[] rawData = e.RawData;
Assert.Null(rawData);
Assert.False(e.CertificateAuthority);
Assert.False(e.HasPathLengthConstraint);
Assert.Equal(0, e.PathLengthConstraint);
}
[Theory]
[MemberData(nameof(BasicConstraintsData))]
public static void BasicConstraintsExtensionEncode(
bool certificateAuthority,
bool hasPathLengthConstraint,
int pathLengthConstraint,
bool critical,
string expectedDerString)
{
X509BasicConstraintsExtension ext = new X509BasicConstraintsExtension(
certificateAuthority,
hasPathLengthConstraint,
pathLengthConstraint,
critical);
byte[] expectedDer = expectedDerString.HexToByteArray();
Assert.Equal(expectedDer, ext.RawData);
}
[Theory]
[MemberData(nameof(BasicConstraintsData))]
public static void BasicConstraintsExtensionDecode(
bool certificateAuthority,
bool hasPathLengthConstraint,
int pathLengthConstraint,
bool critical,
string rawDataString)
{
byte[] rawData = rawDataString.HexToByteArray();
int expectedPathLengthConstraint = hasPathLengthConstraint ? pathLengthConstraint : 0;
X509BasicConstraintsExtension ext = new X509BasicConstraintsExtension(new AsnEncodedData(rawData), critical);
Assert.Equal(certificateAuthority, ext.CertificateAuthority);
Assert.Equal(hasPathLengthConstraint, ext.HasPathLengthConstraint);
Assert.Equal(expectedPathLengthConstraint, ext.PathLengthConstraint);
}
public static object[][] BasicConstraintsData = new object[][]
{
new object[] { false, false, 0, false, "3000" },
new object[] { false, false, 121, false, "3000" },
new object[] { true, false, 0, false, "30030101ff" },
new object[] { false, true, 0, false, "3003020100" },
new object[] { false, true, 7654321, false, "3005020374cbb1" },
new object[] { true, true, 559, false, "30070101ff0202022f" },
};
[Fact]
public static void EnhancedKeyUsageExtensionDefault()
{
X509EnhancedKeyUsageExtension e = new X509EnhancedKeyUsageExtension();
string oidValue = e.Oid.Value;
Assert.Equal("2.5.29.37", oidValue);
byte[] rawData = e.RawData;
Assert.Null(rawData);
OidCollection usages = e.EnhancedKeyUsages;
Assert.Equal(0, usages.Count);
}
[Fact]
public static void EnhancedKeyUsageExtension_Empty()
{
OidCollection usages = new OidCollection();
TestEnhancedKeyUsageExtension(usages, false, "3000".HexToByteArray());
}
[Fact]
public static void EnhancedKeyUsageExtension_2Oids()
{
Oid oid1 = new Oid("1.3.6.1.5.5.7.3.1");
Oid oid2 = new Oid("1.3.6.1.4.1.311.10.3.1");
OidCollection usages = new OidCollection();
usages.Add(oid1);
usages.Add(oid2);
TestEnhancedKeyUsageExtension(usages, false, "301606082b06010505070301060a2b0601040182370a0301".HexToByteArray());
}
[Theory]
[InlineData("1")]
[InlineData("3.0")]
[InlineData("Invalid Value")]
public static void EnhancedKeyUsageExtension_InvalidOid(string invalidOidValue)
{
OidCollection oids = new OidCollection
{
new Oid(invalidOidValue)
};
Assert.ThrowsAny<CryptographicException>(() => new X509EnhancedKeyUsageExtension(oids, false));
}
[Fact]
public static void EnhancedKeyUsageExtension_ImmutableOids()
{
Oid oid1 = new Oid("1.3.6.1.5.5.7.3.1");
OidCollection usages = new OidCollection();
X509EnhancedKeyUsageExtension e = new X509EnhancedKeyUsageExtension(usages, false);
Assert.Equal(0, e.EnhancedKeyUsages.Count);
usages.Add(oid1);
Assert.Equal(0, e.EnhancedKeyUsages.Count);
e.EnhancedKeyUsages.Add(oid1);
Assert.Equal(0, e.EnhancedKeyUsages.Count);
Assert.NotSame(e.EnhancedKeyUsages, e.EnhancedKeyUsages);
}
[Fact]
public static void SubjectKeyIdentifierExtensionDefault()
{
X509SubjectKeyIdentifierExtension e = new X509SubjectKeyIdentifierExtension();
string oidValue = e.Oid.Value;
Assert.Equal("2.5.29.14", oidValue);
byte[] rawData = e.RawData;
Assert.Null(rawData);
string skid = e.SubjectKeyIdentifier;
Assert.Null(skid);
}
[Fact]
public static void SubjectKeyIdentifierExtension_Bytes()
{
byte[] sk = { 1, 2, 3, 4 };
X509SubjectKeyIdentifierExtension e = new X509SubjectKeyIdentifierExtension(sk, false);
byte[] rawData = e.RawData;
Assert.Equal("040401020304".HexToByteArray(), rawData);
e = new X509SubjectKeyIdentifierExtension(new AsnEncodedData(rawData), false);
string skid = e.SubjectKeyIdentifier;
Assert.Equal("01020304", skid);
}
[Fact]
public static void SubjectKeyIdentifierExtension_String()
{
string sk = "01ABcd";
X509SubjectKeyIdentifierExtension e = new X509SubjectKeyIdentifierExtension(sk, false);
byte[] rawData = e.RawData;
Assert.Equal("040301abcd".HexToByteArray(), rawData);
e = new X509SubjectKeyIdentifierExtension(new AsnEncodedData(rawData), false);
string skid = e.SubjectKeyIdentifier;
Assert.Equal("01ABCD", skid);
}
[Fact]
public static void SubjectKeyIdentifierExtension_PublicKey()
{
PublicKey pk;
using (var cert = new X509Certificate2(TestData.MsCertificate))
{
pk = cert.PublicKey;
}
X509SubjectKeyIdentifierExtension e = new X509SubjectKeyIdentifierExtension(pk, false);
byte[] rawData = e.RawData;
Assert.Equal("04145971a65a334dda980780ff841ebe87f9723241f2".HexToByteArray(), rawData);
e = new X509SubjectKeyIdentifierExtension(new AsnEncodedData(rawData), false);
string skid = e.SubjectKeyIdentifier;
Assert.Equal("5971A65A334DDA980780FF841EBE87F9723241F2", skid);
}
[Fact]
public static void SubjectKeyIdentifierExtension_PublicKeySha1()
{
TestSubjectKeyIdentifierExtension(
TestData.MsCertificate,
X509SubjectKeyIdentifierHashAlgorithm.Sha1,
false,
"04145971a65a334dda980780ff841ebe87f9723241f2".HexToByteArray(),
"5971A65A334DDA980780FF841EBE87F9723241F2");
}
[Fact]
public static void SubjectKeyIdentifierExtension_PublicKeyShortSha1()
{
TestSubjectKeyIdentifierExtension(
TestData.MsCertificate,
X509SubjectKeyIdentifierHashAlgorithm.ShortSha1,
false,
"04084ebe87f9723241f2".HexToByteArray(),
"4EBE87F9723241F2");
}
[Fact]
public static void SubjectKeyIdentifierExtension_PublicKeyCapiSha1()
{
TestSubjectKeyIdentifierExtension(
TestData.MsCertificate,
X509SubjectKeyIdentifierHashAlgorithm.CapiSha1,
false,
"0414a260a870be1145ed71e2bb5aa19463a4fe9dcc41".HexToByteArray(),
"A260A870BE1145ED71E2BB5AA19463A4FE9DCC41");
}
[Fact]
public static void ReadInvalidExtension_KeyUsage()
{
X509KeyUsageExtension keyUsageExtension =
new X509KeyUsageExtension(new AsnEncodedData(Array.Empty<byte>()), false);
Assert.ThrowsAny<CryptographicException>(() => keyUsageExtension.KeyUsages);
}
private static void TestKeyUsageExtension(X509KeyUsageFlags flags, bool critical, byte[] expectedDer)
{
X509KeyUsageExtension ext = new X509KeyUsageExtension(flags, critical);
byte[] rawData = ext.RawData;
Assert.Equal(expectedDer, rawData);
// Assert that format doesn't crash
string s = ext.Format(false);
// Rebuild it from the RawData.
ext = new X509KeyUsageExtension(new AsnEncodedData(rawData), critical);
Assert.Equal(flags, ext.KeyUsages);
}
private static void TestEnhancedKeyUsageExtension(
OidCollection usages,
bool critical,
byte[] expectedDer)
{
X509EnhancedKeyUsageExtension ext = new X509EnhancedKeyUsageExtension(usages, critical);
byte[] rawData = ext.RawData;
Assert.Equal(expectedDer, rawData);
ext = new X509EnhancedKeyUsageExtension(new AsnEncodedData(rawData), critical);
OidCollection actualUsages = ext.EnhancedKeyUsages;
Assert.Equal(usages.Count, actualUsages.Count);
for (int i = 0; i < usages.Count; i++)
{
Assert.Equal(usages[i].Value, actualUsages[i].Value);
}
}
private static void TestSubjectKeyIdentifierExtension(
byte[] certBytes,
X509SubjectKeyIdentifierHashAlgorithm algorithm,
bool critical,
byte[] expectedDer,
string expectedIdentifier)
{
PublicKey pk;
using (var cert = new X509Certificate2(certBytes))
{
pk = cert.PublicKey;
}
X509SubjectKeyIdentifierExtension ext =
new X509SubjectKeyIdentifierExtension(pk, algorithm, critical);
byte[] rawData = ext.RawData;
Assert.Equal(expectedDer, rawData);
ext = new X509SubjectKeyIdentifierExtension(new AsnEncodedData(rawData), critical);
Assert.Equal(expectedIdentifier, ext.SubjectKeyIdentifier);
}
}
}