You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
12 KiB
281 lines
12 KiB
8 months ago
|
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||
|
#pragma warning disable
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.IO;
|
||
|
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1;
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509;
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||
|
|
||
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Tls
|
||
|
{
|
||
|
/// <summary>Parsing and encoding of a <i>CertificateRequest</i> struct from RFC 4346.</summary>
|
||
|
/// <remarks>
|
||
|
/// <pre>
|
||
|
/// struct {
|
||
|
/// ClientCertificateType certificate_types<1..2^8-1>;
|
||
|
/// DistinguishedName certificate_authorities<3..2^16-1>;
|
||
|
/// } CertificateRequest;
|
||
|
/// </pre>
|
||
|
/// Updated for RFC 5246:
|
||
|
/// <pre>
|
||
|
/// struct {
|
||
|
/// ClientCertificateType certificate_types <1..2 ^ 8 - 1>;
|
||
|
/// SignatureAndHashAlgorithm supported_signature_algorithms <2 ^ 16 - 1>;
|
||
|
/// DistinguishedName certificate_authorities <0..2 ^ 16 - 1>;
|
||
|
/// } CertificateRequest;
|
||
|
/// </pre>
|
||
|
/// Revised for RFC 8446:
|
||
|
/// <pre>
|
||
|
/// struct {
|
||
|
/// opaque certificate_request_context <0..2 ^ 8 - 1>;
|
||
|
/// Extension extensions <2..2 ^ 16 - 1>;
|
||
|
/// } CertificateRequest;
|
||
|
/// </pre>
|
||
|
/// </remarks>
|
||
|
/// <seealso cref="ClientCertificateType"/>
|
||
|
/// <seealso cref="X509Name"/>
|
||
|
public sealed class CertificateRequest
|
||
|
{
|
||
|
/// <exception cref="IOException"/>
|
||
|
private static IList CheckSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms,
|
||
|
short alertDescription)
|
||
|
{
|
||
|
if (null == supportedSignatureAlgorithms)
|
||
|
throw new TlsFatalAlert(alertDescription, "'signature_algorithms' is required");
|
||
|
|
||
|
return supportedSignatureAlgorithms;
|
||
|
}
|
||
|
|
||
|
private readonly byte[] m_certificateRequestContext;
|
||
|
private readonly short[] m_certificateTypes;
|
||
|
private readonly IList m_supportedSignatureAlgorithms;
|
||
|
private readonly IList m_supportedSignatureAlgorithmsCert;
|
||
|
private readonly IList m_certificateAuthorities;
|
||
|
|
||
|
/// <param name="certificateTypes">see <see cref="ClientCertificateType"/> for valid constants.</param>
|
||
|
/// <param name="supportedSignatureAlgorithms"></param>
|
||
|
/// <param name="certificateAuthorities">an <see cref="IList"/> of <see cref="X509Name"/>.</param>
|
||
|
public CertificateRequest(short[] certificateTypes, IList supportedSignatureAlgorithms,
|
||
|
IList certificateAuthorities)
|
||
|
: this(null, certificateTypes, supportedSignatureAlgorithms, null, certificateAuthorities)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// TODO[tls13] Prefer to manage the certificateRequestContext internally only?
|
||
|
/// <exception cref="IOException"/>
|
||
|
public CertificateRequest(byte[] certificateRequestContext, IList supportedSignatureAlgorithms,
|
||
|
IList supportedSignatureAlgorithmsCert, IList certificateAuthorities)
|
||
|
: this(certificateRequestContext, null,
|
||
|
CheckSupportedSignatureAlgorithms(supportedSignatureAlgorithms, AlertDescription.internal_error),
|
||
|
supportedSignatureAlgorithmsCert, certificateAuthorities)
|
||
|
{
|
||
|
/*
|
||
|
* TODO[tls13] Removed certificateTypes, added certificate_request_context, added extensions
|
||
|
* (required: signature_algorithms, optional: status_request, signed_certificate_timestamp,
|
||
|
* certificate_authorities, oid_filters, signature_algorithms_cert)
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
private CertificateRequest(byte[] certificateRequestContext, short[] certificateTypes,
|
||
|
IList supportedSignatureAlgorithms, IList supportedSignatureAlgorithmsCert, IList certificateAuthorities)
|
||
|
{
|
||
|
if (null != certificateRequestContext && !TlsUtilities.IsValidUint8(certificateRequestContext.Length))
|
||
|
throw new ArgumentException("cannot be longer than 255", "certificateRequestContext");
|
||
|
if (null != certificateTypes
|
||
|
&& (certificateTypes.Length < 1 || !TlsUtilities.IsValidUint8(certificateTypes.Length)))
|
||
|
{
|
||
|
throw new ArgumentException("should have length from 1 to 255", "certificateTypes");
|
||
|
}
|
||
|
|
||
|
this.m_certificateRequestContext = TlsUtilities.Clone(certificateRequestContext);
|
||
|
this.m_certificateTypes = certificateTypes;
|
||
|
this.m_supportedSignatureAlgorithms = supportedSignatureAlgorithms;
|
||
|
this.m_supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert;
|
||
|
this.m_certificateAuthorities = certificateAuthorities;
|
||
|
}
|
||
|
|
||
|
public byte[] GetCertificateRequestContext()
|
||
|
{
|
||
|
return TlsUtilities.Clone(m_certificateRequestContext);
|
||
|
}
|
||
|
|
||
|
/// <returns>an array of certificate types</returns>
|
||
|
/// <seealso cref="ClientCertificateType"/>
|
||
|
public short[] CertificateTypes
|
||
|
{
|
||
|
get { return m_certificateTypes; }
|
||
|
}
|
||
|
|
||
|
/// <returns>an <see cref="IList"/> of <see cref="SignatureAndHashAlgorithm"/> (or null before TLS 1.2).
|
||
|
/// </returns>
|
||
|
public IList SupportedSignatureAlgorithms
|
||
|
{
|
||
|
get { return m_supportedSignatureAlgorithms; }
|
||
|
}
|
||
|
|
||
|
/// <returns>an optional <see cref="IList"/> of <see cref="SignatureAndHashAlgorithm"/>. May be non-null from
|
||
|
/// TLS 1.3 onwards.</returns>
|
||
|
public IList SupportedSignatureAlgorithmsCert
|
||
|
{
|
||
|
get { return m_supportedSignatureAlgorithmsCert; }
|
||
|
}
|
||
|
|
||
|
/// <returns>an <see cref="IList"/> of <see cref="X509Name"/>.</returns>
|
||
|
public IList CertificateAuthorities
|
||
|
{
|
||
|
get { return m_certificateAuthorities; }
|
||
|
}
|
||
|
|
||
|
public bool HasCertificateRequestContext(byte[] certificateRequestContext)
|
||
|
{
|
||
|
return Arrays.AreEqual(m_certificateRequestContext, certificateRequestContext);
|
||
|
}
|
||
|
|
||
|
/// <summary>Encode this <see cref="CertificateRequest"/> to a <see cref="Stream"/>.</summary>
|
||
|
/// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
|
||
|
/// <param name="output">the <see cref="Stream"/> to encode to.</param>
|
||
|
/// <exception cref="IOException"/>
|
||
|
public void Encode(TlsContext context, Stream output)
|
||
|
{
|
||
|
ProtocolVersion negotiatedVersion = context.ServerVersion;
|
||
|
bool isTlsV12 = TlsUtilities.IsTlsV12(negotiatedVersion);
|
||
|
bool isTlsV13 = TlsUtilities.IsTlsV13(negotiatedVersion);
|
||
|
|
||
|
if (isTlsV13 != (null != m_certificateRequestContext) ||
|
||
|
isTlsV13 != (null == m_certificateTypes) ||
|
||
|
isTlsV12 != (null != m_supportedSignatureAlgorithms) ||
|
||
|
(!isTlsV13 && (null != m_supportedSignatureAlgorithmsCert)))
|
||
|
{
|
||
|
throw new InvalidOperationException();
|
||
|
}
|
||
|
|
||
|
if (isTlsV13)
|
||
|
{
|
||
|
TlsUtilities.WriteOpaque8(m_certificateRequestContext, output);
|
||
|
|
||
|
IDictionary extensions = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
|
||
|
TlsExtensionsUtilities.AddSignatureAlgorithmsExtension(extensions, m_supportedSignatureAlgorithms);
|
||
|
|
||
|
if (null != m_supportedSignatureAlgorithmsCert)
|
||
|
{
|
||
|
TlsExtensionsUtilities.AddSignatureAlgorithmsCertExtension(extensions,
|
||
|
m_supportedSignatureAlgorithmsCert);
|
||
|
}
|
||
|
|
||
|
if (null != m_certificateAuthorities)
|
||
|
{
|
||
|
TlsExtensionsUtilities.AddCertificateAuthoritiesExtension(extensions, m_certificateAuthorities);
|
||
|
}
|
||
|
|
||
|
byte[] extEncoding = TlsProtocol.WriteExtensionsData(extensions);
|
||
|
|
||
|
TlsUtilities.WriteOpaque16(extEncoding, output);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
TlsUtilities.WriteUint8ArrayWithUint8Length(m_certificateTypes, output);
|
||
|
|
||
|
if (isTlsV12)
|
||
|
{
|
||
|
// TODO Check whether SignatureAlgorithm.anonymous is allowed here
|
||
|
TlsUtilities.EncodeSupportedSignatureAlgorithms(m_supportedSignatureAlgorithms, output);
|
||
|
}
|
||
|
|
||
|
if (m_certificateAuthorities == null || m_certificateAuthorities.Count < 1)
|
||
|
{
|
||
|
TlsUtilities.WriteUint16(0, output);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IList derEncodings = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(m_certificateAuthorities.Count);
|
||
|
|
||
|
int totalLength = 0;
|
||
|
foreach (X509Name certificateAuthority in m_certificateAuthorities)
|
||
|
{
|
||
|
byte[] derEncoding = certificateAuthority.GetEncoded(Asn1Encodable.Der);
|
||
|
derEncodings.Add(derEncoding);
|
||
|
totalLength += derEncoding.Length + 2;
|
||
|
}
|
||
|
|
||
|
TlsUtilities.CheckUint16(totalLength);
|
||
|
TlsUtilities.WriteUint16(totalLength, output);
|
||
|
|
||
|
foreach (byte[] derEncoding in derEncodings)
|
||
|
{
|
||
|
TlsUtilities.WriteOpaque16(derEncoding, output);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>Parse a <see cref="CertificateRequest"/> from a <see cref="Stream"/></summary>
|
||
|
/// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
|
||
|
/// <param name="input">the <see cref="Stream"/> to parse from.</param>
|
||
|
/// <returns>a <see cref="CertificateRequest"/> object.</returns>
|
||
|
/// <exception cref="IOException"/>
|
||
|
public static CertificateRequest Parse(TlsContext context, Stream input)
|
||
|
{
|
||
|
ProtocolVersion negotiatedVersion = context.ServerVersion;
|
||
|
bool isTlsV13 = TlsUtilities.IsTlsV13(negotiatedVersion);
|
||
|
|
||
|
if (isTlsV13)
|
||
|
{
|
||
|
byte[] certificateRequestContext = TlsUtilities.ReadOpaque8(input);
|
||
|
|
||
|
/*
|
||
|
* TODO[tls13] required: signature_algorithms; optional: status_request,
|
||
|
* signed_certificate_timestamp, certificate_authorities, oid_filters,
|
||
|
* signature_algorithms_cert
|
||
|
*/
|
||
|
|
||
|
byte[] extEncoding = TlsUtilities.ReadOpaque16(input);
|
||
|
|
||
|
IDictionary extensions = TlsProtocol.ReadExtensionsData13(HandshakeType.certificate_request,
|
||
|
extEncoding);
|
||
|
|
||
|
IList supportedSignatureAlgorithms13 = CheckSupportedSignatureAlgorithms(
|
||
|
TlsExtensionsUtilities.GetSignatureAlgorithmsExtension(extensions),
|
||
|
AlertDescription.missing_extension);
|
||
|
IList supportedSignatureAlgorithmsCert13 = TlsExtensionsUtilities
|
||
|
.GetSignatureAlgorithmsCertExtension(extensions);
|
||
|
IList certificateAuthorities13 = TlsExtensionsUtilities.GetCertificateAuthoritiesExtension(extensions);
|
||
|
|
||
|
return new CertificateRequest(certificateRequestContext, supportedSignatureAlgorithms13,
|
||
|
supportedSignatureAlgorithmsCert13, certificateAuthorities13);
|
||
|
}
|
||
|
|
||
|
bool isTLSv12 = TlsUtilities.IsTlsV12(negotiatedVersion);
|
||
|
|
||
|
short[] certificateTypes = TlsUtilities.ReadUint8ArrayWithUint8Length(input, 1);
|
||
|
|
||
|
IList supportedSignatureAlgorithms = null;
|
||
|
if (isTLSv12)
|
||
|
{
|
||
|
supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(input);
|
||
|
}
|
||
|
|
||
|
IList certificateAuthorities = null;
|
||
|
{
|
||
|
byte[] certAuthData = TlsUtilities.ReadOpaque16(input);
|
||
|
if (certAuthData.Length > 0)
|
||
|
{
|
||
|
certificateAuthorities = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
|
||
|
MemoryStream bis = new MemoryStream(certAuthData, false);
|
||
|
do
|
||
|
{
|
||
|
byte[] derEncoding = TlsUtilities.ReadOpaque16(bis, 1);
|
||
|
Asn1Object asn1 = TlsUtilities.ReadDerObject(derEncoding);
|
||
|
certificateAuthorities.Add(X509Name.GetInstance(asn1));
|
||
|
}
|
||
|
while (bis.Position < bis.Length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#pragma warning restore
|
||
|
#endif
|