diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-07-12 15:15:36 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-07-12 15:15:36 +0700 |
commit | 68c795fe81277f73aeb90d8ad4c6f4305f32c906 (patch) | |
tree | 59643344aafef91bbd4c4a3a7973deba3d837a00 /crypto/src/tls/CertificateRequest.cs | |
parent | TLS test tweaks (diff) | |
download | BouncyCastle.NET-ed25519-68c795fe81277f73aeb90d8ad4c6f4305f32c906.tar.xz |
Port of new TLS API from bc-java
Diffstat (limited to 'crypto/src/tls/CertificateRequest.cs')
-rw-r--r-- | crypto/src/tls/CertificateRequest.cs | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/crypto/src/tls/CertificateRequest.cs b/crypto/src/tls/CertificateRequest.cs new file mode 100644 index 000000000..1abf01aed --- /dev/null +++ b/crypto/src/tls/CertificateRequest.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities; + +namespace 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 = 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 = 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 = 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); + } + } +} |