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.
452 lines
16 KiB
452 lines
16 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
using System.Collections; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security.Certificates; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.X509; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.X509.Store; |
|
|
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Pkix |
|
{ |
|
/** |
|
* The <i>Service Provider Interface</i> (<b>SPI</b>) |
|
* for the {@link CertPathValidator CertPathValidator} class. All |
|
* <code>CertPathValidator</code> implementations must include a class (the |
|
* SPI class) that extends this class (<code>CertPathValidatorSpi</code>) |
|
* and implements all of its methods. In general, instances of this class |
|
* should only be accessed through the <code>CertPathValidator</code> class. |
|
* For details, see the Java Cryptography Architecture.<br /> |
|
* <br /> |
|
* <b>Concurrent Access</b><br /> |
|
* <br /> |
|
* Instances of this class need not be protected against concurrent |
|
* access from multiple threads. Threads that need to access a single |
|
* <code>CertPathValidatorSpi</code> instance concurrently should synchronize |
|
* amongst themselves and provide the necessary locking before calling the |
|
* wrapping <code>CertPathValidator</code> object.<br /> |
|
* <br /> |
|
* However, implementations of <code>CertPathValidatorSpi</code> may still |
|
* encounter concurrency issues, since multiple threads each |
|
* manipulating a different <code>CertPathValidatorSpi</code> instance need not |
|
* synchronize. |
|
*/ |
|
/// <summary> |
|
/// CertPathValidatorSpi implementation for X.509 Certificate validation a la RFC |
|
/// 3280. |
|
/// </summary> |
|
public class PkixCertPathValidator |
|
{ |
|
public virtual PkixCertPathValidatorResult Validate( |
|
PkixCertPath certPath, |
|
PkixParameters paramsPkix) |
|
{ |
|
if (paramsPkix.GetTrustAnchors() == null) |
|
{ |
|
throw new ArgumentException( |
|
"trustAnchors is null, this is not allowed for certification path validation.", |
|
"parameters"); |
|
} |
|
|
|
// |
|
// 6.1.1 - inputs |
|
// |
|
|
|
// |
|
// (a) |
|
// |
|
IList certs = certPath.Certificates; |
|
int n = certs.Count; |
|
|
|
if (certs.Count == 0) |
|
throw new PkixCertPathValidatorException("Certification path is empty.", null, certPath, 0); |
|
|
|
// |
|
// (b) |
|
// |
|
// DateTime validDate = PkixCertPathValidatorUtilities.GetValidDate(paramsPkix); |
|
|
|
// |
|
// (c) |
|
// |
|
ISet userInitialPolicySet = paramsPkix.GetInitialPolicies(); |
|
|
|
// |
|
// (d) |
|
// |
|
TrustAnchor trust; |
|
try |
|
{ |
|
trust = PkixCertPathValidatorUtilities.FindTrustAnchor( |
|
(X509Certificate)certs[certs.Count - 1], |
|
paramsPkix.GetTrustAnchors()); |
|
|
|
if (trust == null) |
|
throw new PkixCertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1); |
|
|
|
CheckCertificate(trust.TrustedCert); |
|
} |
|
catch (Exception e) |
|
{ |
|
throw new PkixCertPathValidatorException(e.Message, e.InnerException, certPath, certs.Count - 1); |
|
} |
|
|
|
// |
|
// (e), (f), (g) are part of the paramsPkix object. |
|
// |
|
IEnumerator certIter; |
|
int index = 0; |
|
int i; |
|
// Certificate for each interation of the validation loop |
|
// Signature information for each iteration of the validation loop |
|
// |
|
// 6.1.2 - setup |
|
// |
|
|
|
// |
|
// (a) |
|
// |
|
IList[] policyNodes = new IList[n + 1]; |
|
for (int j = 0; j < policyNodes.Length; j++) |
|
{ |
|
policyNodes[j] = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(); |
|
} |
|
|
|
ISet policySet = new HashSet(); |
|
|
|
policySet.Add(Rfc3280CertPathUtilities.ANY_POLICY); |
|
|
|
PkixPolicyNode validPolicyTree = new PkixPolicyNode(BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(), 0, policySet, null, new HashSet(), |
|
Rfc3280CertPathUtilities.ANY_POLICY, false); |
|
|
|
policyNodes[0].Add(validPolicyTree); |
|
|
|
// |
|
// (b) and (c) |
|
// |
|
PkixNameConstraintValidator nameConstraintValidator = new PkixNameConstraintValidator(); |
|
|
|
// (d) |
|
// |
|
int explicitPolicy; |
|
ISet acceptablePolicies = new HashSet(); |
|
|
|
if (paramsPkix.IsExplicitPolicyRequired) |
|
{ |
|
explicitPolicy = 0; |
|
} |
|
else |
|
{ |
|
explicitPolicy = n + 1; |
|
} |
|
|
|
// |
|
// (e) |
|
// |
|
int inhibitAnyPolicy; |
|
|
|
if (paramsPkix.IsAnyPolicyInhibited) |
|
{ |
|
inhibitAnyPolicy = 0; |
|
} |
|
else |
|
{ |
|
inhibitAnyPolicy = n + 1; |
|
} |
|
|
|
// |
|
// (f) |
|
// |
|
int policyMapping; |
|
|
|
if (paramsPkix.IsPolicyMappingInhibited) |
|
{ |
|
policyMapping = 0; |
|
} |
|
else |
|
{ |
|
policyMapping = n + 1; |
|
} |
|
|
|
// |
|
// (g), (h), (i), (j) |
|
// |
|
AsymmetricKeyParameter workingPublicKey; |
|
X509Name workingIssuerName; |
|
|
|
X509Certificate sign = trust.TrustedCert; |
|
try |
|
{ |
|
if (sign != null) |
|
{ |
|
workingIssuerName = sign.SubjectDN; |
|
workingPublicKey = sign.GetPublicKey(); |
|
} |
|
else |
|
{ |
|
workingIssuerName = new X509Name(trust.CAName); |
|
workingPublicKey = trust.CAPublicKey; |
|
} |
|
} |
|
catch (ArgumentException ex) |
|
{ |
|
throw new PkixCertPathValidatorException("Subject of trust anchor could not be (re)encoded.", ex, certPath, |
|
-1); |
|
} |
|
|
|
AlgorithmIdentifier workingAlgId = null; |
|
try |
|
{ |
|
workingAlgId = PkixCertPathValidatorUtilities.GetAlgorithmIdentifier(workingPublicKey); |
|
} |
|
catch (PkixCertPathValidatorException e) |
|
{ |
|
throw new PkixCertPathValidatorException( |
|
"Algorithm identifier of public key of trust anchor could not be read.", e, certPath, -1); |
|
} |
|
|
|
// DerObjectIdentifier workingPublicKeyAlgorithm = workingAlgId.Algorithm; |
|
// Asn1Encodable workingPublicKeyParameters = workingAlgId.Parameters; |
|
|
|
// |
|
// (k) |
|
// |
|
int maxPathLength = n; |
|
|
|
// |
|
// 6.1.3 |
|
// |
|
|
|
X509CertStoreSelector certConstraints = paramsPkix.GetTargetCertConstraints(); |
|
if (certConstraints != null && !certConstraints.Match((X509Certificate)certs[0])) |
|
{ |
|
throw new PkixCertPathValidatorException( |
|
"Target certificate in certification path does not match targetConstraints.", null, certPath, 0); |
|
} |
|
|
|
// |
|
// initialize CertPathChecker's |
|
// |
|
IList pathCheckers = paramsPkix.GetCertPathCheckers(); |
|
certIter = pathCheckers.GetEnumerator(); |
|
|
|
while (certIter.MoveNext()) |
|
{ |
|
((PkixCertPathChecker)certIter.Current).Init(false); |
|
} |
|
|
|
X509Certificate cert = null; |
|
|
|
for (index = certs.Count - 1; index >= 0; index--) |
|
{ |
|
// try |
|
// { |
|
// |
|
// i as defined in the algorithm description |
|
// |
|
i = n - index; |
|
|
|
// |
|
// set certificate to be checked in this round |
|
// sign and workingPublicKey and workingIssuerName are set |
|
// at the end of the for loop and initialized the |
|
// first time from the TrustAnchor |
|
// |
|
cert = (X509Certificate)certs[index]; |
|
|
|
try |
|
{ |
|
CheckCertificate(cert); |
|
} |
|
catch (Exception e) |
|
{ |
|
throw new PkixCertPathValidatorException(e.Message, e.InnerException, certPath, index); |
|
} |
|
|
|
// |
|
// 6.1.3 |
|
// |
|
|
|
Rfc3280CertPathUtilities.ProcessCertA(certPath, paramsPkix, index, workingPublicKey, |
|
workingIssuerName, sign); |
|
|
|
Rfc3280CertPathUtilities.ProcessCertBC(certPath, index, nameConstraintValidator); |
|
|
|
validPolicyTree = Rfc3280CertPathUtilities.ProcessCertD(certPath, index, |
|
acceptablePolicies, validPolicyTree, policyNodes, inhibitAnyPolicy); |
|
|
|
validPolicyTree = Rfc3280CertPathUtilities.ProcessCertE(certPath, index, validPolicyTree); |
|
|
|
Rfc3280CertPathUtilities.ProcessCertF(certPath, index, validPolicyTree, explicitPolicy); |
|
|
|
// |
|
// 6.1.4 |
|
// |
|
|
|
if (i != n) |
|
{ |
|
if (cert != null && cert.Version == 1) |
|
{ |
|
// we've found the trust anchor at the top of the path, ignore and keep going |
|
if ((i == 1) && cert.Equals(trust.TrustedCert)) |
|
continue; |
|
|
|
throw new PkixCertPathValidatorException( |
|
"Version 1 certificates can't be used as CA ones.", null, certPath, index); |
|
} |
|
|
|
Rfc3280CertPathUtilities.PrepareNextCertA(certPath, index); |
|
|
|
validPolicyTree = Rfc3280CertPathUtilities.PrepareCertB(certPath, index, policyNodes, |
|
validPolicyTree, policyMapping); |
|
|
|
Rfc3280CertPathUtilities.PrepareNextCertG(certPath, index, nameConstraintValidator); |
|
|
|
// (h) |
|
explicitPolicy = Rfc3280CertPathUtilities.PrepareNextCertH1(certPath, index, explicitPolicy); |
|
policyMapping = Rfc3280CertPathUtilities.PrepareNextCertH2(certPath, index, policyMapping); |
|
inhibitAnyPolicy = Rfc3280CertPathUtilities.PrepareNextCertH3(certPath, index, inhibitAnyPolicy); |
|
|
|
// |
|
// (i) |
|
// |
|
explicitPolicy = Rfc3280CertPathUtilities.PrepareNextCertI1(certPath, index, explicitPolicy); |
|
policyMapping = Rfc3280CertPathUtilities.PrepareNextCertI2(certPath, index, policyMapping); |
|
|
|
// (j) |
|
inhibitAnyPolicy = Rfc3280CertPathUtilities.PrepareNextCertJ(certPath, index, inhibitAnyPolicy); |
|
|
|
// (k) |
|
Rfc3280CertPathUtilities.PrepareNextCertK(certPath, index); |
|
|
|
// (l) |
|
maxPathLength = Rfc3280CertPathUtilities.PrepareNextCertL(certPath, index, maxPathLength); |
|
|
|
// (m) |
|
maxPathLength = Rfc3280CertPathUtilities.PrepareNextCertM(certPath, index, maxPathLength); |
|
|
|
// (n) |
|
Rfc3280CertPathUtilities.PrepareNextCertN(certPath, index); |
|
|
|
ISet criticalExtensions1 = cert.GetCriticalExtensionOids(); |
|
|
|
if (criticalExtensions1 != null) |
|
{ |
|
criticalExtensions1 = new HashSet(criticalExtensions1); |
|
|
|
// these extensions are handled by the algorithm |
|
criticalExtensions1.Remove(X509Extensions.KeyUsage.Id); |
|
criticalExtensions1.Remove(X509Extensions.CertificatePolicies.Id); |
|
criticalExtensions1.Remove(X509Extensions.PolicyMappings.Id); |
|
criticalExtensions1.Remove(X509Extensions.InhibitAnyPolicy.Id); |
|
criticalExtensions1.Remove(X509Extensions.IssuingDistributionPoint.Id); |
|
criticalExtensions1.Remove(X509Extensions.DeltaCrlIndicator.Id); |
|
criticalExtensions1.Remove(X509Extensions.PolicyConstraints.Id); |
|
criticalExtensions1.Remove(X509Extensions.BasicConstraints.Id); |
|
criticalExtensions1.Remove(X509Extensions.SubjectAlternativeName.Id); |
|
criticalExtensions1.Remove(X509Extensions.NameConstraints.Id); |
|
} |
|
else |
|
{ |
|
criticalExtensions1 = new HashSet(); |
|
} |
|
|
|
// (o) |
|
Rfc3280CertPathUtilities.PrepareNextCertO(certPath, index, criticalExtensions1, pathCheckers); |
|
|
|
// set signing certificate for next round |
|
sign = cert; |
|
|
|
// (c) |
|
workingIssuerName = sign.SubjectDN; |
|
|
|
// (d) |
|
try |
|
{ |
|
workingPublicKey = PkixCertPathValidatorUtilities.GetNextWorkingKey(certPath.Certificates, index); |
|
} |
|
catch (PkixCertPathValidatorException e) |
|
{ |
|
throw new PkixCertPathValidatorException("Next working key could not be retrieved.", e, certPath, index); |
|
} |
|
|
|
workingAlgId = PkixCertPathValidatorUtilities.GetAlgorithmIdentifier(workingPublicKey); |
|
// (f) |
|
// workingPublicKeyAlgorithm = workingAlgId.Algorithm; |
|
// (e) |
|
// workingPublicKeyParameters = workingAlgId.Parameters; |
|
} |
|
} |
|
|
|
// |
|
// 6.1.5 Wrap-up procedure |
|
// |
|
|
|
explicitPolicy = Rfc3280CertPathUtilities.WrapupCertA(explicitPolicy, cert); |
|
|
|
explicitPolicy = Rfc3280CertPathUtilities.WrapupCertB(certPath, index + 1, explicitPolicy); |
|
|
|
// |
|
// (c) (d) and (e) are already done |
|
// |
|
|
|
// |
|
// (f) |
|
// |
|
ISet criticalExtensions = cert.GetCriticalExtensionOids(); |
|
|
|
if (criticalExtensions != null) |
|
{ |
|
criticalExtensions = new HashSet(criticalExtensions); |
|
|
|
// Requires .Id |
|
// these extensions are handled by the algorithm |
|
criticalExtensions.Remove(X509Extensions.KeyUsage.Id); |
|
criticalExtensions.Remove(X509Extensions.CertificatePolicies.Id); |
|
criticalExtensions.Remove(X509Extensions.PolicyMappings.Id); |
|
criticalExtensions.Remove(X509Extensions.InhibitAnyPolicy.Id); |
|
criticalExtensions.Remove(X509Extensions.IssuingDistributionPoint.Id); |
|
criticalExtensions.Remove(X509Extensions.DeltaCrlIndicator.Id); |
|
criticalExtensions.Remove(X509Extensions.PolicyConstraints.Id); |
|
criticalExtensions.Remove(X509Extensions.BasicConstraints.Id); |
|
criticalExtensions.Remove(X509Extensions.SubjectAlternativeName.Id); |
|
criticalExtensions.Remove(X509Extensions.NameConstraints.Id); |
|
criticalExtensions.Remove(X509Extensions.CrlDistributionPoints.Id); |
|
} |
|
else |
|
{ |
|
criticalExtensions = new HashSet(); |
|
} |
|
|
|
Rfc3280CertPathUtilities.WrapupCertF(certPath, index + 1, pathCheckers, criticalExtensions); |
|
|
|
PkixPolicyNode intersection = Rfc3280CertPathUtilities.WrapupCertG(certPath, paramsPkix, userInitialPolicySet, |
|
index + 1, policyNodes, validPolicyTree, acceptablePolicies); |
|
|
|
if ((explicitPolicy > 0) || (intersection != null)) |
|
{ |
|
return new PkixCertPathValidatorResult(trust, intersection, cert.GetPublicKey()); |
|
} |
|
|
|
throw new PkixCertPathValidatorException("Path processing failed on policy.", null, certPath, index); |
|
} |
|
|
|
internal static void CheckCertificate(X509Certificate cert) |
|
{ |
|
try |
|
{ |
|
TbsCertificateStructure.GetInstance(cert.CertificateStructure.TbsCertificate); |
|
} |
|
catch (CertificateEncodingException e) |
|
{ |
|
throw new Exception("unable to process TBSCertificate", e); |
|
} |
|
} |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|