|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Formats.Asn1;
using System.Security.Cryptography.X509Certificates.Asn1;
namespace System.Security.Cryptography.X509Certificates
{
public sealed class X509BasicConstraintsExtension : X509Extension
{
public X509BasicConstraintsExtension()
: base(Oids.BasicConstraints2Oid)
{
_decoded = true;
}
public X509BasicConstraintsExtension(bool certificateAuthority, bool hasPathLengthConstraint, int pathLengthConstraint, bool critical)
: base(Oids.BasicConstraints2Oid, EncodeExtension(certificateAuthority, hasPathLengthConstraint, pathLengthConstraint), critical, skipCopy: true)
{
}
public X509BasicConstraintsExtension(AsnEncodedData encodedBasicConstraints, bool critical)
: base(Oids.BasicConstraints2Oid, encodedBasicConstraints.RawData, critical)
{
}
public bool CertificateAuthority
{
get
{
if (!_decoded)
DecodeExtension();
return _certificateAuthority;
}
}
public bool HasPathLengthConstraint
{
get
{
if (!_decoded)
DecodeExtension();
return _hasPathLenConstraint;
}
}
public int PathLengthConstraint
{
get
{
if (!_decoded)
DecodeExtension();
return _pathLenConstraint;
}
}
public override void CopyFrom(AsnEncodedData asnEncodedData)
{
base.CopyFrom(asnEncodedData);
_decoded = false;
}
/// <summary>
/// Creates an instance of <see cref="X509BasicConstraintsExtension"/> appropriate for
/// a certificate authority, optionally including a path length constraint value.
/// </summary>
/// <param name="pathLengthConstraint">
/// The longest valid length of a certificate chain between the certificate containing
/// this extension and an end-entity certificate.
/// The default is <see langword="null" />, an unconstrained length.
/// </param>
/// <returns>
/// The configured basic constraints extension.
/// </returns>
/// <remarks>
/// Following the guidance from IETF RFC 3280, the extension returned from this method
/// will have the <see cref="X509Extension.Critical"/> property set to <see langword="true" />.
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="pathLengthConstraint"/> is a non-null value less than zero.
/// </exception>
public static X509BasicConstraintsExtension CreateForCertificateAuthority(int? pathLengthConstraint = null)
{
return new X509BasicConstraintsExtension(
true,
pathLengthConstraint.HasValue,
pathLengthConstraint.GetValueOrDefault(),
true);
}
/// <summary>
/// Creates an instance of <see cref="X509BasicConstraintsExtension"/> appropriate for
/// an end-entity certificate, optionally marking the extension as critical.
/// </summary>
/// <param name="critical">
/// <see langword="true" /> to mark the extension as critical; <see langword="false" /> otherwise.
/// The default is <see langword="false" />.
/// </param>
/// <returns>
/// The configured basic constraints extension.
/// </returns>
public static X509BasicConstraintsExtension CreateForEndEntity(bool critical = false)
{
return new X509BasicConstraintsExtension(false, false, 0, critical);
}
private static byte[] EncodeExtension(bool certificateAuthority, bool hasPathLengthConstraint, int pathLengthConstraint)
{
if (hasPathLengthConstraint)
{
ArgumentOutOfRangeException.ThrowIfNegative(pathLengthConstraint);
}
return EncodeX509BasicConstraints2Extension(certificateAuthority, hasPathLengthConstraint, pathLengthConstraint);
}
private void DecodeExtension()
{
if (Oid!.Value == Oids.BasicConstraints)
{
LegacyBasicConstraintsDecoder.DecodeX509BasicConstraintsExtension(
RawData,
out _certificateAuthority,
out _hasPathLenConstraint,
out _pathLenConstraint);
}
else
{
DecodeX509BasicConstraints2Extension(
RawData,
out _certificateAuthority,
out _hasPathLenConstraint,
out _pathLenConstraint);
}
_decoded = true;
}
private static byte[] EncodeX509BasicConstraints2Extension(
bool certificateAuthority,
bool hasPathLengthConstraint,
int pathLengthConstraint)
{
BasicConstraintsAsn constraints = default;
constraints.CA = certificateAuthority;
if (hasPathLengthConstraint)
{
constraints.PathLengthConstraint = pathLengthConstraint;
}
// Largest possible encoded extension is 11 bytes when pathLenConstraint is int.MaxValue.
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER, initialCapacity: 11);
constraints.Encode(writer);
return writer.Encode();
}
private static void DecodeX509BasicConstraints2Extension(
byte[] encoded,
out bool certificateAuthority,
out bool hasPathLengthConstraint,
out int pathLengthConstraint)
{
BasicConstraintsAsn constraints = BasicConstraintsAsn.Decode(encoded, AsnEncodingRules.BER);
certificateAuthority = constraints.CA;
hasPathLengthConstraint = constraints.PathLengthConstraint.HasValue;
pathLengthConstraint = constraints.PathLengthConstraint.GetValueOrDefault();
}
private bool _certificateAuthority;
private bool _hasPathLenConstraint;
private int _pathLenConstraint;
private bool _decoded;
}
}
|