Example #1
0
        /// <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);
        }
Example #2
0
        /// <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();
            }
        }