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.
280 lines
12 KiB
280 lines
12 KiB
#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
|
|
|