add all
This commit is contained in:
300
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/tls/Certificate.cs
vendored
Normal file
300
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/tls/Certificate.cs
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
|
||||
|
||||
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Tls
|
||||
{
|
||||
/// <summary>Parsing and encoding of a <i>Certificate</i> struct from RFC 4346.</summary>
|
||||
/// <remarks>
|
||||
/// <pre>
|
||||
/// opaque ASN.1Cert<2^24-1>;
|
||||
/// struct {
|
||||
/// ASN.1Cert certificate_list<0..2^24-1>;
|
||||
/// } Certificate;
|
||||
/// </pre>
|
||||
/// </remarks>
|
||||
public sealed class Certificate
|
||||
{
|
||||
private static readonly TlsCertificate[] EmptyCerts = new TlsCertificate[0];
|
||||
private static readonly CertificateEntry[] EmptyCertEntries = new CertificateEntry[0];
|
||||
|
||||
public static readonly Certificate EmptyChain = new Certificate(EmptyCerts);
|
||||
public static readonly Certificate EmptyChainTls13 = new Certificate(TlsUtilities.EmptyBytes, EmptyCertEntries);
|
||||
|
||||
public sealed class ParseOptions
|
||||
{
|
||||
public short CertificateType { get; set; } = Tls.CertificateType.X509;
|
||||
public int MaxChainLength { get; set; } = int.MaxValue;
|
||||
}
|
||||
|
||||
private static CertificateEntry[] Convert(TlsCertificate[] certificateList)
|
||||
{
|
||||
if (TlsUtilities.IsNullOrContainsNull(certificateList))
|
||||
throw new ArgumentException("cannot be null or contain any nulls", "certificateList");
|
||||
|
||||
int count = certificateList.Length;
|
||||
CertificateEntry[] result = new CertificateEntry[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
result[i] = new CertificateEntry(certificateList[i], null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private readonly byte[] m_certificateRequestContext;
|
||||
private readonly CertificateEntry[] m_certificateEntryList;
|
||||
private readonly short m_certificateType;
|
||||
|
||||
public Certificate(TlsCertificate[] certificateList)
|
||||
: this(null, Convert(certificateList))
|
||||
{
|
||||
}
|
||||
|
||||
public Certificate(byte[] certificateRequestContext, CertificateEntry[] certificateEntryList)
|
||||
: this(Tls.CertificateType.X509, certificateRequestContext, certificateEntryList)
|
||||
{
|
||||
}
|
||||
|
||||
// TODO[tls13] Prefer to manage the certificateRequestContext internally only?
|
||||
public Certificate(short certificateType, byte[] certificateRequestContext, CertificateEntry[] certificateEntryList)
|
||||
{
|
||||
if (null != certificateRequestContext && !TlsUtilities.IsValidUint8(certificateRequestContext.Length))
|
||||
throw new ArgumentException("cannot be longer than 255", "certificateRequestContext");
|
||||
if (TlsUtilities.IsNullOrContainsNull(certificateEntryList))
|
||||
throw new ArgumentException("cannot be null or contain any nulls", "certificateEntryList");
|
||||
|
||||
m_certificateRequestContext = TlsUtilities.Clone(certificateRequestContext);
|
||||
m_certificateEntryList = certificateEntryList;
|
||||
m_certificateType = certificateType;
|
||||
}
|
||||
|
||||
public byte[] GetCertificateRequestContext()
|
||||
{
|
||||
return TlsUtilities.Clone(m_certificateRequestContext);
|
||||
}
|
||||
|
||||
/// <returns>an array of <see cref="TlsCertificate"/> representing a certificate chain.</returns>
|
||||
public TlsCertificate[] GetCertificateList()
|
||||
{
|
||||
return CloneCertificateList();
|
||||
}
|
||||
|
||||
public TlsCertificate GetCertificateAt(int index)
|
||||
{
|
||||
return m_certificateEntryList[index].Certificate;
|
||||
}
|
||||
|
||||
public CertificateEntry GetCertificateEntryAt(int index)
|
||||
{
|
||||
return m_certificateEntryList[index];
|
||||
}
|
||||
|
||||
public CertificateEntry[] GetCertificateEntryList()
|
||||
{
|
||||
return CloneCertificateEntryList();
|
||||
}
|
||||
|
||||
public short CertificateType => m_certificateType;
|
||||
|
||||
public int Length => m_certificateEntryList.Length;
|
||||
|
||||
/// <returns><c>true</c> if this certificate chain contains no certificates, or <c>false</c> otherwise.
|
||||
/// </returns>
|
||||
public bool IsEmpty => m_certificateEntryList.Length == 0;
|
||||
|
||||
/// <summary>Encode this <see cref="Certificate"/> to a <see cref="Stream"/>, and optionally calculate the
|
||||
/// "end point hash" (per RFC 5929's tls-server-end-point binding).</summary>
|
||||
/// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
|
||||
/// <param name="messageOutput">the <see cref="Stream"/> to encode to.</param>
|
||||
/// <param name="endPointHashOutput">the <see cref="Stream"/> to write the "end point hash" to (or null).
|
||||
/// </param>
|
||||
/// <exception cref="IOException"/>
|
||||
public void Encode(TlsContext context, Stream messageOutput, Stream endPointHashOutput)
|
||||
{
|
||||
bool isTlsV13 = TlsUtilities.IsTlsV13(context);
|
||||
|
||||
if ((null != m_certificateRequestContext) != isTlsV13)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if (isTlsV13)
|
||||
{
|
||||
TlsUtilities.WriteOpaque8(m_certificateRequestContext, messageOutput);
|
||||
}
|
||||
|
||||
int count = m_certificateEntryList.Length;
|
||||
var certEncodings = new List<byte[]>(count);
|
||||
var extEncodings = isTlsV13 ? new List<byte[]>(count) : null;
|
||||
|
||||
long totalLength = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
CertificateEntry entry = m_certificateEntryList[i];
|
||||
TlsCertificate cert = entry.Certificate;
|
||||
byte[] derEncoding = cert.GetEncoded();
|
||||
|
||||
if (i == 0 && endPointHashOutput != null)
|
||||
{
|
||||
CalculateEndPointHash(context, cert, derEncoding, endPointHashOutput);
|
||||
}
|
||||
|
||||
certEncodings.Add(derEncoding);
|
||||
totalLength += derEncoding.Length;
|
||||
totalLength += 3;
|
||||
|
||||
if (isTlsV13)
|
||||
{
|
||||
var extensions = entry.Extensions;
|
||||
byte[] extEncoding = (null == extensions)
|
||||
? TlsUtilities.EmptyBytes
|
||||
: TlsProtocol.WriteExtensionsData(extensions);
|
||||
|
||||
extEncodings.Add(extEncoding);
|
||||
totalLength += extEncoding.Length;
|
||||
totalLength += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 7250 indicates the raw key is not wrapped in a cert list like X509 is
|
||||
// but RFC 8446 wraps it in a CertificateEntry, which is inside certificate_list
|
||||
if (isTlsV13 || m_certificateType != Tls.CertificateType.RawPublicKey)
|
||||
{
|
||||
TlsUtilities.CheckUint24(totalLength);
|
||||
TlsUtilities.WriteUint24((int)totalLength, messageOutput);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
TlsUtilities.WriteOpaque24(certEncodings[i], messageOutput);
|
||||
|
||||
if (isTlsV13)
|
||||
{
|
||||
TlsUtilities.WriteOpaque16(extEncodings[i], messageOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Parse a <see cref="Certificate"/> from a <see cref="Stream"/>.</summary>
|
||||
/// <param name="options">the <see cref="ParseOptions"/> to apply during parsing.</param>
|
||||
/// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
|
||||
/// <param name="messageInput">the <see cref="Stream"/> to parse from.</param>
|
||||
/// <param name="endPointHashOutput">the <see cref="Stream"/> to write the "end point hash" to (or null).
|
||||
/// </param>
|
||||
/// <returns>a <see cref="Certificate"/> object.</returns>
|
||||
/// <exception cref="IOException"/>
|
||||
public static Certificate Parse(ParseOptions options, TlsContext context, Stream messageInput,
|
||||
Stream endPointHashOutput)
|
||||
{
|
||||
SecurityParameters securityParameters = context.SecurityParameters;
|
||||
bool isTlsV13 = TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion);
|
||||
short certType = options.CertificateType;
|
||||
|
||||
byte[] certificateRequestContext = null;
|
||||
if (isTlsV13)
|
||||
{
|
||||
certificateRequestContext = TlsUtilities.ReadOpaque8(messageInput);
|
||||
}
|
||||
|
||||
int totalLength = TlsUtilities.ReadUint24(messageInput);
|
||||
if (totalLength == 0)
|
||||
{
|
||||
return !isTlsV13 ? EmptyChain
|
||||
: certificateRequestContext.Length < 1 ? EmptyChainTls13
|
||||
: new Certificate(certType, certificateRequestContext, EmptyCertEntries);
|
||||
}
|
||||
|
||||
byte[] certListData = TlsUtilities.ReadFully(totalLength, messageInput);
|
||||
MemoryStream buf = new MemoryStream(certListData, false);
|
||||
|
||||
TlsCrypto crypto = context.Crypto;
|
||||
int maxChainLength = System.Math.Max(1, options.MaxChainLength);
|
||||
|
||||
var certificate_list = new List<CertificateEntry>();
|
||||
while (buf.Position < buf.Length)
|
||||
{
|
||||
if (certificate_list.Count >= maxChainLength)
|
||||
{
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error,
|
||||
"Certificate chain longer than maximum (" + maxChainLength + ")");
|
||||
}
|
||||
|
||||
// RFC 7250 indicates the raw key is not wrapped in a cert list like X509 is
|
||||
// but RFC 8446 wraps it in a CertificateEntry, which is inside certificate_list
|
||||
byte[] derEncoding;
|
||||
if (isTlsV13 || certType != Tls.CertificateType.RawPublicKey)
|
||||
{
|
||||
derEncoding = TlsUtilities.ReadOpaque24(buf, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
derEncoding = certListData;
|
||||
buf.Seek(totalLength, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
TlsCertificate cert = crypto.CreateCertificate(certType, derEncoding);
|
||||
|
||||
if (certificate_list.Count < 1 && endPointHashOutput != null)
|
||||
{
|
||||
CalculateEndPointHash(context, cert, derEncoding, endPointHashOutput);
|
||||
}
|
||||
|
||||
IDictionary<int, byte[]> extensions = null;
|
||||
if (isTlsV13)
|
||||
{
|
||||
byte[] extEncoding = TlsUtilities.ReadOpaque16(buf);
|
||||
|
||||
extensions = TlsProtocol.ReadExtensionsData13(HandshakeType.certificate, extEncoding);
|
||||
}
|
||||
|
||||
certificate_list.Add(new CertificateEntry(cert, extensions));
|
||||
}
|
||||
|
||||
CertificateEntry[] certificateList = new CertificateEntry[certificate_list.Count];
|
||||
for (int i = 0; i < certificate_list.Count; i++)
|
||||
{
|
||||
certificateList[i] = (CertificateEntry)certificate_list[i];
|
||||
}
|
||||
|
||||
return new Certificate(certType, certificateRequestContext, certificateList);
|
||||
}
|
||||
|
||||
private static void CalculateEndPointHash(TlsContext context, TlsCertificate cert, byte[] encoding,
|
||||
Stream output)
|
||||
{
|
||||
byte[] endPointHash = TlsUtilities.CalculateEndPointHash(context, cert, encoding);
|
||||
if (endPointHash != null && endPointHash.Length > 0)
|
||||
{
|
||||
output.Write(endPointHash, 0, endPointHash.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private TlsCertificate[] CloneCertificateList()
|
||||
{
|
||||
int count = m_certificateEntryList.Length;
|
||||
if (0 == count)
|
||||
return EmptyCerts;
|
||||
|
||||
TlsCertificate[] result = new TlsCertificate[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
result[i] = m_certificateEntryList[i].Certificate;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private CertificateEntry[] CloneCertificateEntryList()
|
||||
{
|
||||
int count = m_certificateEntryList.Length;
|
||||
if (0 == count)
|
||||
return EmptyCertEntries;
|
||||
|
||||
CertificateEntry[] result = new CertificateEntry[count];
|
||||
Array.Copy(m_certificateEntryList, 0, result, 0, count);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
Reference in New Issue
Block a user