/// <summary> /// Adapted from .NET source <see cref="System.Security.Cryptography.X509Certificates.X509Utils.CopyOidsToUnmanagedMemory"/> /// </summary> private static SafeLocalAllocHandle CopyOidsToUnmanagedMemory(OidCollection oids) { SafeLocalAllocHandle safeLocalAllocHandle = SafeLocalAllocHandle.InvalidHandle; if (oids == null || oids.Count == 0) { return(safeLocalAllocHandle); } // Copy the oid strings to a local list to prevent a security race condition where // the OidCollection or individual oids can be modified by another thread and // potentially cause a buffer overflow List <string> oidStrs = oids.Cast <Oid>().Select(oid => oid.Value).ToList(); IntPtr pOid = IntPtr.Zero; // Needs to be checked to avoid having large sets of oids overflow the sizes and allow // a potential buffer overflow checked { int ptrSize = oidStrs.Count * Marshal.SizeOf(typeof(IntPtr)); int oidSize = oidStrs.Sum(oidStr => oidStr.Length + 1); safeLocalAllocHandle = NativeMethods.LocalAlloc(NativeMethods.LPTR, new IntPtr((uint)ptrSize + (uint)oidSize)); pOid = new IntPtr((long)safeLocalAllocHandle.DangerousGetHandle() + ptrSize); } for (int index = 0; index < oidStrs.Count; index++) { Marshal.WriteIntPtr(new IntPtr((long)safeLocalAllocHandle.DangerousGetHandle() + index * Marshal.SizeOf(typeof(IntPtr))), pOid); byte[] ansiOid = Encoding.ASCII.GetBytes(oidStrs[index]); Marshal.Copy(ansiOid, 0, pOid, ansiOid.Length); pOid = new IntPtr((long)pOid + oidStrs[index].Length + 1); } return(safeLocalAllocHandle); }
/// <summary> /// Builds and validates a certificate chain, similar to <see cref="X509Chain.Build" />. /// </summary> /// <param name="certificate">The certificate to validate.</param> /// <param name="chainPolicy">The chain policy to apply.</param> /// <param name="chain">The resulting chain, whose <see cref="X509Chain.ChainStatus"/> and <see cref="X509Chain.ChainElements"/> properties are populated.</param> /// <returns>Whether the certificate is valid accoring to the given <paramref name="chainPolicy"/></returns> /// <exception cref="System.Security.Cryptography.CryptographicException"> /// When any of the underlying Windows CryptoAPI calls fail. /// </exception> public unsafe bool BuildChain(X509Certificate2 certificate, X509ChainPolicy chainPolicy, out X509Chain chain) { SafeX509ChainHandle ppChainContext = SafeX509ChainHandle.InvalidHandle; SafeCertStoreHandle hCertStore = SafeCertStoreHandle.InvalidHandle; if (chainPolicy.ExtraStore != null && chainPolicy.ExtraStore.Count > 0) { hCertStore = ExportToMemoryStore(chainPolicy.ExtraStore); } NativeMethods.CERT_CHAIN_PARA chainPara = new NativeMethods.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 (chainPolicy.ApplicationPolicy != null && chainPolicy.ApplicationPolicy.Count > 0) { chainPara.RequestedUsage.dwType = NativeMethods.USAGE_MATCH_TYPE_AND; chainPara.RequestedUsage.Usage.cUsageIdentifier = (uint)chainPolicy.ApplicationPolicy.Count; applicationPolicyHandle = CopyOidsToUnmanagedMemory(chainPolicy.ApplicationPolicy); chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = applicationPolicyHandle.DangerousGetHandle(); } // Certificate policy if (chainPolicy.CertificatePolicy != null && chainPolicy.CertificatePolicy.Count > 0) { chainPara.RequestedIssuancePolicy.dwType = NativeMethods.USAGE_MATCH_TYPE_AND; chainPara.RequestedIssuancePolicy.Usage.cUsageIdentifier = (uint)chainPolicy.CertificatePolicy.Count; certificatePolicyHandle = CopyOidsToUnmanagedMemory(chainPolicy.CertificatePolicy); chainPara.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = certificatePolicyHandle.DangerousGetHandle(); } chainPara.dwUrlRetrievalTimeout = (uint)Math.Floor(chainPolicy.UrlRetrievalTimeout.TotalMilliseconds); FILETIME ft = new FILETIME(); *(long *)&ft = chainPolicy.VerificationTime.ToFileTime(); uint flags = MapRevocationFlags(chainPolicy.RevocationMode, chainPolicy.RevocationFlag); using (SafeCertContextHandle certContextHandle = NativeMethods.CertDuplicateCertificateContext(certificate.Handle)) { // Build the chain. if (!NativeMethods.CertGetCertificateChain(hChainEngine: this.safeChainEngineHandle, pCertContext: certContextHandle, pTime: ref ft, hAdditionalStore: hCertStore, pChainPara: ref chainPara, dwFlags: flags, pvReserved: IntPtr.Zero, ppChainContext: ref ppChainContext)) { throw new CryptographicException(Marshal.GetHRForLastWin32Error()); } chain = new X509Chain(ppChainContext.DangerousGetHandle()) { ChainPolicy = chainPolicy }; // Verify the chain using the specified policy. NativeMethods.CERT_CHAIN_POLICY_PARA policyPara = new NativeMethods.CERT_CHAIN_POLICY_PARA(Marshal.SizeOf(typeof(NativeMethods.CERT_CHAIN_POLICY_PARA))); NativeMethods.CERT_CHAIN_POLICY_STATUS policyStatus = new NativeMethods.CERT_CHAIN_POLICY_STATUS(Marshal.SizeOf(typeof(NativeMethods.CERT_CHAIN_POLICY_STATUS))); policyPara.dwFlags = (uint)chainPolicy.VerificationFlags; if (!NativeMethods.CertVerifyCertificateChainPolicy( pszPolicyOID: new IntPtr(NativeMethods.CERT_CHAIN_POLICY_BASE), pChainContext: ppChainContext, pPolicyPara: ref policyPara, pPolicyStatus: ref policyStatus)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } NativeMethods.SetLastError(policyStatus.dwError); return(policyStatus.dwError == 0); } } finally { applicationPolicyHandle.Dispose(); certificatePolicyHandle.Dispose(); } }