|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
//
// X509Chain.cs
//
namespace System.Security.Cryptography.X509Certificates {
using System.Collections;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using Microsoft.Win32.SafeHandles;
using _FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
[Flags]
public enum X509ChainStatusFlags {
NoError = 0x00000000,
NotTimeValid = 0x00000001,
NotTimeNested = 0x00000002,
Revoked = 0x00000004,
NotSignatureValid = 0x00000008,
NotValidForUsage = 0x00000010,
UntrustedRoot = 0x00000020,
RevocationStatusUnknown = 0x00000040,
Cyclic = 0x00000080,
InvalidExtension = 0x00000100,
InvalidPolicyConstraints = 0x00000200,
InvalidBasicConstraints = 0x00000400,
InvalidNameConstraints = 0x00000800,
HasNotSupportedNameConstraint = 0x00001000,
HasNotDefinedNameConstraint = 0x00002000,
HasNotPermittedNameConstraint = 0x00004000,
HasExcludedNameConstraint = 0x00008000,
PartialChain = 0x00010000,
CtlNotTimeValid = 0x00020000,
CtlNotSignatureValid = 0x00040000,
CtlNotValidForUsage = 0x00080000,
OfflineRevocation = 0x01000000,
NoIssuanceChainPolicy = 0x02000000,
ExplicitDistrust = 0x04000000,
HasNotSupportedCriticalExtension = 0x08000000,
HasWeakSignature = 0x00100000,
}
public struct X509ChainStatus {
private X509ChainStatusFlags m_status;
private string m_statusInformation;
public X509ChainStatusFlags Status {
get {
return m_status;
}
set {
m_status = value;
}
}
public string StatusInformation {
get {
if (m_statusInformation == null)
return String.Empty;
return m_statusInformation;
}
set {
m_statusInformation = value;
}
}
}
public class X509Chain
: IDisposable
{
private uint m_status;
private X509ChainPolicy m_chainPolicy;
private X509ChainStatus[] m_chainStatus;
private X509ChainElementCollection m_chainElementCollection;
[SecurityCritical]
private SafeX509ChainHandle m_safeCertChainHandle;
private bool m_useMachineContext;
private readonly object m_syncRoot = new object();
public static X509Chain Create() {
return (X509Chain) CryptoConfig.CreateFromName("X509Chain");
}
[SecurityCritical]
public X509Chain () : this (false) {}
[SecurityCritical]
public X509Chain (bool useMachineContext) {
m_status = 0;
m_chainPolicy = null;
m_chainStatus = null;
m_chainElementCollection = new X509ChainElementCollection();
m_safeCertChainHandle = SafeX509ChainHandle.InvalidHandle;
m_useMachineContext = useMachineContext;
}
// Package protected constructor for creating a chain from a PCCERT_CHAIN_CONTEXT
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
public X509Chain (IntPtr chainContext) {
if (chainContext == IntPtr.Zero)
throw new ArgumentNullException("chainContext");
m_safeCertChainHandle = CAPI.CertDuplicateCertificateChain(chainContext);
if (m_safeCertChainHandle == null || m_safeCertChainHandle == SafeX509ChainHandle.InvalidHandle)
throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidContextHandle), "chainContext");
Init();
}
public IntPtr ChainContext {
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
get {
return m_safeCertChainHandle.DangerousGetHandle();
}
}
public SafeX509ChainHandle SafeHandle {
[SecurityCritical]
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
get
{
return m_safeCertChainHandle;
}
}
public X509ChainPolicy ChainPolicy {
get {
if (m_chainPolicy == null)
m_chainPolicy = new X509ChainPolicy();
return m_chainPolicy;
}
set {
if (value == null)
throw new ArgumentNullException("value");
m_chainPolicy = value;
}
}
public X509ChainStatus[] ChainStatus {
get {
// We give the user a reference to the array since we'll never access it.
if (m_chainStatus == null) {
if (m_status == 0) {
m_chainStatus = new X509ChainStatus[0]; // empty array
} else {
m_chainStatus = GetChainStatusInformation(m_status);
}
}
return m_chainStatus;
}
}
public X509ChainElementCollection ChainElements {
get {
return m_chainElementCollection;
}
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
#if !FEATURE_CORESYSTEM
[PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
[PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted=true)]
#endif
public bool Build (X509Certificate2 certificate) {
lock (m_syncRoot) {
if (certificate == null || certificate.CertContext.IsInvalid)
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidContextHandle), "certificate");
#if !FEATURE_CORESYSTEM
// Chain building opens and enumerates the root store to see if the root of the chain is trusted.
StorePermission sp = new StorePermission(StorePermissionFlags.OpenStore | StorePermissionFlags.EnumerateCertificates);
sp.Demand();
#endif
X509ChainPolicy chainPolicy = this.ChainPolicy;
#if !FEATURE_CORESYSTEM
if (chainPolicy.RevocationMode == X509RevocationMode.Online) {
if (certificate.Extensions[CAPI.szOID_CRL_DIST_POINTS] != null ||
certificate.Extensions[CAPI.szOID_AUTHORITY_INFO_ACCESS] != null) {
// If there is a CDP or AIA extension, we demand unrestricted network access and store add permission
// since CAPI can download certificates into the CA store from the network.
PermissionSet ps = new PermissionSet(PermissionState.None);
ps.AddPermission(new WebPermission(PermissionState.Unrestricted));
ps.AddPermission(new StorePermission(StorePermissionFlags.AddToStore));
ps.Demand();
}
}
#endif
Reset();
int hr = BuildChain(m_useMachineContext ? new IntPtr(CAPI.HCCE_LOCAL_MACHINE) : new IntPtr(CAPI.HCCE_CURRENT_USER),
certificate.CertContext,
chainPolicy.ExtraStore,
chainPolicy.ApplicationPolicy,
chainPolicy.CertificatePolicy,
chainPolicy.RevocationMode,
chainPolicy.RevocationFlag,
chainPolicy.VerificationTime,
chainPolicy.UrlRetrievalTimeout,
ref m_safeCertChainHandle);
if (hr != CAPI.S_OK)
return false;
// Init.
Init();
// Verify the chain using the specified policy.
CAPI.CERT_CHAIN_POLICY_PARA PolicyPara = new CAPI.CERT_CHAIN_POLICY_PARA(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_PARA)));
CAPI.CERT_CHAIN_POLICY_STATUS PolicyStatus = new CAPI.CERT_CHAIN_POLICY_STATUS(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_STATUS)));
PolicyPara.dwFlags = (uint) chainPolicy.VerificationFlags;
if (!CAPI.CertVerifyCertificateChainPolicy(new IntPtr(CAPI.CERT_CHAIN_POLICY_BASE),
m_safeCertChainHandle,
ref PolicyPara,
ref PolicyStatus))
// The API failed.
throw new CryptographicException(Marshal.GetLastWin32Error());
CAPI.SetLastError(PolicyStatus.dwError);
return (PolicyStatus.dwError == 0);
}
}
[SecurityCritical]
#if !FEATURE_CORESYSTEM
[PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
[PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted=true)]
#endif
public void Reset () {
m_status = 0;
m_chainStatus = null;
m_chainElementCollection = new X509ChainElementCollection();
if (!m_safeCertChainHandle.IsInvalid) {
m_safeCertChainHandle.Dispose();
m_safeCertChainHandle = SafeX509ChainHandle.InvalidHandle;
}
}
[SecuritySafeCritical]
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
[SecuritySafeCritical]
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Reset();
}
}
[SecurityCritical]
private unsafe void Init () {
using (SafeX509ChainHandle safeCertChainHandle = CAPI.CertDuplicateCertificateChain(m_safeCertChainHandle)) {
CAPI.CERT_CHAIN_CONTEXT pChain = new CAPI.CERT_CHAIN_CONTEXT(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_CONTEXT)));
uint cbSize = (uint) Marshal.ReadInt32(safeCertChainHandle.DangerousGetHandle());
if (cbSize > Marshal.SizeOf(pChain))
cbSize = (uint) Marshal.SizeOf(pChain);
X509Utils.memcpy(m_safeCertChainHandle.DangerousGetHandle(), new IntPtr(&pChain), cbSize);
m_status = pChain.dwErrorStatus;
Debug.Assert(pChain.cChain > 0);
m_chainElementCollection = new X509ChainElementCollection(Marshal.ReadIntPtr(pChain.rgpChain));
}
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
internal static X509ChainStatus[] GetChainStatusInformation (uint dwStatus) {
if (dwStatus == 0)
return new X509ChainStatus[0];
int count = 0;
for (uint bits = dwStatus; bits != 0; bits = bits >> 1) {
if ((bits & 0x1) != 0)
count++;
}
X509ChainStatus[] chainStatus = new X509ChainStatus[count];
int index = 0;
foreach (X509ChainErrorMapping mapping in s_x509ChainErrorMappings)
{
if ((dwStatus & mapping.Win32Flag) != 0)
{
Debug.Assert(index < chainStatus.Length);
chainStatus[index].StatusInformation = X509Utils.GetSystemErrorString(mapping.Win32ErrorCode);
chainStatus[index].Status = mapping.ChainStatusFlag;
index++;
dwStatus &= ~mapping.Win32Flag;
}
}
int shiftCount = 0;
for (uint bits = dwStatus; bits != 0; bits = bits >> 1) {
if ((bits & 0x1) != 0) {
Debug.Assert(index < chainStatus.Length);
chainStatus[index].Status = (X509ChainStatusFlags) (1 << shiftCount);
chainStatus[index].StatusInformation = SR.GetString(SR.Unknown_Error);
index++;
}
shiftCount++;
}
Debug.Assert(index == chainStatus.Length);
return chainStatus;
}
//
// Builds a certificate chain.
//
[SecurityCritical]
internal static unsafe int BuildChain (IntPtr hChainEngine,
SafeCertContextHandle pCertContext,
X509Certificate2Collection extraStore,
OidCollection applicationPolicy,
OidCollection certificatePolicy,
X509RevocationMode revocationMode,
X509RevocationFlag revocationFlag,
DateTime verificationTime,
TimeSpan timeout,
ref SafeX509ChainHandle ppChainContext) {
if (pCertContext == null || pCertContext.IsInvalid)
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidContextHandle), "pCertContext");
SafeCertStoreHandle hCertStore = SafeCertStoreHandle.InvalidHandle;
if (extraStore != null && extraStore.Count > 0)
hCertStore = X509Utils.ExportToMemoryStore(extraStore);
CAPI.CERT_CHAIN_PARA ChainPara = new CAPI.CERT_CHAIN_PARA();
// Initialize the structure size.
ChainPara.cbSize = (uint) Marshal.SizeOf(ChainPara);
SafeLocalAllocHandle applicationPolicyHandle = SafeLocalAllocHandle.InvalidHandle;
SafeLocalAllocHandle certificatePolicyHandle = SafeLocalAllocHandle.InvalidHandle;
try {
// Application policy
if (applicationPolicy != null && applicationPolicy.Count > 0) {
ChainPara.RequestedUsage.dwType = CAPI.USAGE_MATCH_TYPE_AND;
ChainPara.RequestedUsage.Usage.cUsageIdentifier = (uint) applicationPolicy.Count;
applicationPolicyHandle = X509Utils.CopyOidsToUnmanagedMemory(applicationPolicy);
ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = applicationPolicyHandle.DangerousGetHandle();
}
// Certificate policy
if (certificatePolicy != null && certificatePolicy.Count > 0) {
ChainPara.RequestedIssuancePolicy.dwType = CAPI.USAGE_MATCH_TYPE_AND;
ChainPara.RequestedIssuancePolicy.Usage.cUsageIdentifier = (uint) certificatePolicy.Count;
certificatePolicyHandle = X509Utils.CopyOidsToUnmanagedMemory(certificatePolicy);
ChainPara.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = certificatePolicyHandle.DangerousGetHandle();
}
ChainPara.dwUrlRetrievalTimeout = (uint) Math.Floor(timeout.TotalMilliseconds);
_FILETIME ft = new _FILETIME();
*((long*) &ft) = verificationTime.ToFileTime();
uint flags = X509Utils.MapRevocationFlags(revocationMode, revocationFlag);
// Build the chain.
if (!CAPI.CertGetCertificateChain(hChainEngine,
pCertContext,
ref ft,
hCertStore,
ref ChainPara,
flags,
IntPtr.Zero,
ref ppChainContext))
return Marshal.GetHRForLastWin32Error();
}
finally {
applicationPolicyHandle.Dispose();
certificatePolicyHandle.Dispose();
}
return CAPI.S_OK;
}
private struct X509ChainErrorMapping
{
public readonly uint Win32Flag;
public readonly int Win32ErrorCode;
public readonly X509ChainStatusFlags ChainStatusFlag;
public X509ChainErrorMapping(uint win32Flag, int win32ErrorCode, X509ChainStatusFlags chainStatusFlag)
{
Win32Flag = win32Flag;
Win32ErrorCode = win32ErrorCode;
ChainStatusFlag = chainStatusFlag;
}
}
private static readonly X509ChainErrorMapping[] s_x509ChainErrorMappings = new[]
{
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_SIGNATURE_VALID, CAPI.TRUST_E_CERT_SIGNATURE, X509ChainStatusFlags.NotSignatureValid),
new X509ChainErrorMapping(CAPI.CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID, CAPI.TRUST_E_CERT_SIGNATURE, X509ChainStatusFlags.CtlNotSignatureValid),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_UNTRUSTED_ROOT, CAPI.CERT_E_UNTRUSTEDROOT, X509ChainStatusFlags.UntrustedRoot),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_PARTIAL_CHAIN, CAPI.CERT_E_CHAINING, X509ChainStatusFlags.PartialChain),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_REVOKED, CAPI.CRYPT_E_REVOKED, X509ChainStatusFlags.Revoked),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_VALID_FOR_USAGE, CAPI.CERT_E_WRONG_USAGE, X509ChainStatusFlags.NotValidForUsage),
new X509ChainErrorMapping(CAPI.CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE, CAPI.CERT_E_WRONG_USAGE, X509ChainStatusFlags.CtlNotValidForUsage),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_TIME_VALID, CAPI.CERT_E_EXPIRED, X509ChainStatusFlags.NotTimeValid),
new X509ChainErrorMapping(CAPI.CERT_TRUST_CTL_IS_NOT_TIME_VALID, CAPI.CERT_E_EXPIRED, X509ChainStatusFlags.CtlNotTimeValid),
new X509ChainErrorMapping(CAPI.CERT_TRUST_INVALID_NAME_CONSTRAINTS, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.InvalidNameConstraints),
new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasNotSupportedNameConstraint),
new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasNotDefinedNameConstraint),
new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasNotPermittedNameConstraint),
new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasExcludedNameConstraint),
new X509ChainErrorMapping(CAPI.CERT_TRUST_INVALID_POLICY_CONSTRAINTS, CAPI.CERT_E_INVALID_POLICY, X509ChainStatusFlags.InvalidPolicyConstraints),
new X509ChainErrorMapping(CAPI.CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY, CAPI.CERT_E_INVALID_POLICY, X509ChainStatusFlags.NoIssuanceChainPolicy),
new X509ChainErrorMapping(CAPI.CERT_TRUST_INVALID_BASIC_CONSTRAINTS, CAPI.TRUST_E_BASIC_CONSTRAINTS, X509ChainStatusFlags.InvalidBasicConstraints),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_TIME_NESTED, CAPI.CERT_E_VALIDITYPERIODNESTING, X509ChainStatusFlags.NotTimeNested),
new X509ChainErrorMapping(CAPI.CERT_TRUST_REVOCATION_STATUS_UNKNOWN, CAPI.CRYPT_E_NO_REVOCATION_CHECK, X509ChainStatusFlags.RevocationStatusUnknown),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_OFFLINE_REVOCATION, CAPI.CRYPT_E_REVOCATION_OFFLINE, X509ChainStatusFlags.OfflineRevocation),
new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_EXPLICIT_DISTRUST, CAPI.TRUST_E_EXPLICIT_DISTRUST, X509ChainStatusFlags.ExplicitDistrust),
new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT, CAPI.CERT_E_CRITICAL, X509ChainStatusFlags.HasNotSupportedCriticalExtension),
new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_WEAK_SIGNATURE, CAPI.CERTSRV_E_WEAK_SIGNATURE_OR_KEY, X509ChainStatusFlags.HasWeakSignature),
};
}
}
|