using System;
using System.Collections;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Utilities.Collections;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
namespace 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());
}
catch (Exception e)
{
throw new PkixCertPathValidatorException(e.Message, e, certPath, certs.Count - 1);
}
if (trust == null)
throw new PkixCertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -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 ArrayList[n + 1];
for (int j = 0; j < policyNodes.Length; j++)
{
policyNodes[j] = new ArrayList();
}
ISet policySet = new HashSet();
policySet.Add(Rfc3280CertPathUtilities.ANY_POLICY);
PkixPolicyNode validPolicyTree = new PkixPolicyNode(new ArrayList(), 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.ObjectID;
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];
//
// 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)
{
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.ObjectID;
// (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);
}
}
}
|