예제 #1
0
        /// <summary>
        /// Validates a certificate.
        /// </summary>
        /// <remarks>
        /// Each UA application may have a list of trusted certificates that is different from
        /// all other UA applications that may be running on the same machine. As a result, the
        /// certificate validator cannot rely completely on the Windows certificate store and
        /// user or machine specific CTLs (certificate trust lists).
        ///
        /// The validator constructs the trust chain for the certificate and follows the chain
        /// until it finds a certification that is in the application trust list. Non-fatal trust
        /// chain errors (i.e. certificate expired) are ignored if the certificate is in the
        /// application trust list.
        ///
        /// If no certificate in the chain is trusted then the validator will still accept the
        /// certification if there are no trust chain errors.
        ///
        /// The validator may be configured to ignore the application trust list and/or trust chain.
        /// </remarks>
        public virtual void Validate(X509Certificate2Collection chain)
        {
            X509Certificate2 certificate = chain[0];

            try
            {
                lock (m_lock)
                {
                    InternalValidate(chain).GetAwaiter().GetResult();

                    // add to list of validated certificates.
                    m_validatedCertificates[certificate.Thumbprint] = new X509Certificate2(certificate.RawData);
                }
            }
            catch (ServiceResultException se)
            {
                // check for errors that may be suppressed.
                switch (se.StatusCode)
                {
                case StatusCodes.BadCertificateHostNameInvalid:
                case StatusCodes.BadCertificateIssuerRevocationUnknown:
                case StatusCodes.BadCertificateChainIncomplete:
                case StatusCodes.BadCertificateIssuerTimeInvalid:
                case StatusCodes.BadCertificateIssuerUseNotAllowed:
                case StatusCodes.BadCertificateRevocationUnknown:
                case StatusCodes.BadCertificateTimeInvalid:
                case StatusCodes.BadCertificatePolicyCheckFailed:
                case StatusCodes.BadCertificateUseNotAllowed:
                case StatusCodes.BadCertificateUntrusted:
                {
                    Utils.Trace("Certificate Vaildation failed for '{0}'. Reason={1}", certificate.Subject, (StatusCode)se.StatusCode);
                    break;
                }

                default:
                {
                    // write the invalid certificate to rejected store if specified.
                    Utils.Trace((int)Utils.TraceMasks.Error, "Certificate '{0}' rejected. Reason={1}", certificate.Subject, (StatusCode)se.StatusCode);
                    SaveCertificate(certificate);

                    throw new ServiceResultException(se, StatusCodes.BadCertificateInvalid);
                }
                }

                // invoke callback.
                bool accept = false;

                lock (m_callbackLock)
                {
                    if (m_CertificateValidation != null)
                    {
                        CertificateValidationEventArgs args = new CertificateValidationEventArgs(new ServiceResult(se), certificate);
                        m_CertificateValidation(this, args);
                        accept = args.Accept;
                    }
                }

                // throw if rejected.
                if (!accept)
                {
                    // write the invalid certificate to rejected store if specified.
                    Utils.Trace((int)Utils.TraceMasks.Error, "Certificate '{0}' rejected. Reason={1}", certificate.Subject, (StatusCode)se.StatusCode);
                    SaveCertificate(certificate);

                    throw new ServiceResultException(se, StatusCodes.BadCertificateInvalid);
                }

                // add to list of peers.
                lock (m_lock)
                {
                    Utils.Trace("Validation error suppressed for '{0}'.", certificate.Subject);
                    m_validatedCertificates[certificate.Thumbprint] = new X509Certificate2(certificate.RawData);
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Checks if issuer has revoked the certificate.
        /// </summary>
        public StatusCode IsRevoked(X509Certificate2 issuer, X509Certificate2 certificate)
        {
            if (issuer == null)
            {
                throw new ArgumentNullException("issuer");
            }

            if (certificate == null)
            {
                throw new ArgumentNullException("certificate");
            }

            // check for CRL.
            DirectoryInfo info = new DirectoryInfo(this.Directory.FullName + "\\crl");

            if (info.Exists)
            {
                bool crlExpired = true;

                foreach (FileInfo file in info.GetFiles("*.crl"))
                {
                    X509CRL crl = null;

                    try
                    {
                        crl = new X509CRL(file.FullName);
                    }
                    catch (Exception e)
                    {
                        Utils.Trace(e, "Could not parse CRL file.");
                        continue;
                    }

                    if (!Utils.CompareDistinguishedName(crl.Issuer, issuer.Subject))
                    {
                        continue;
                    }

                    if (!crl.VerifySignature(issuer, false))
                    {
                        continue;
                    }

                    if (crl.IsRevoked(certificate))
                    {
                        return(StatusCodes.BadCertificateRevoked);
                    }

                    if (crl.UpdateTime <= DateTime.UtcNow && (crl.NextUpdateTime == DateTime.MinValue || crl.NextUpdateTime >= DateTime.UtcNow))
                    {
                        crlExpired = false;
                    }
                }

                // certificate is fine.
                if (!crlExpired)
                {
                    return(StatusCodes.Good);
                }
            }

            // can't find a valid CRL.
            return(StatusCodes.BadCertificateRevocationUnknown);
        }
예제 #3
0
        /// <summary>
        /// Reads the current contents of the directory from disk.
        /// </summary>
        private IDictionary <string, Entry> Load(string thumbprint)
        {
            lock (m_lock)
            {
                DateTime now = DateTime.UtcNow;

                // refresh the directories.
                m_certificateSubdir.Refresh();

                if (!NoPrivateKeys)
                {
                    m_privateKeySubdir.Refresh();
                }

                // check if store exists.
                if (!m_certificateSubdir.Exists)
                {
                    m_certificates.Clear();
                    return(m_certificates);
                }

                // check if cache is still good.
                if (m_certificateSubdir.LastWriteTimeUtc < m_lastDirectoryCheck && (NoPrivateKeys || this.m_privateKeySubdir.LastWriteTimeUtc < m_lastDirectoryCheck))
                {
                    return(m_certificates);
                }

                m_certificates.Clear();
                m_lastDirectoryCheck = now;
                bool incompleteSearch = false;

                // check for public keys.
                foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der"))
                {
                    try
                    {
                        Entry entry = new Entry();

                        entry.Certificate               = new X509Certificate2(file.FullName);
                        entry.CertificateFile           = file;
                        entry.PrivateKeyFile            = null;
                        entry.CertificateWithPrivateKey = null;

                        if (!NoPrivateKeys)
                        {
                            string fileRoot = file.Name.Substring(0, entry.CertificateFile.Name.Length - entry.CertificateFile.Extension.Length);

                            StringBuilder filePath = new StringBuilder();
                            filePath.Append(m_privateKeySubdir.FullName);
                            filePath.Append("\\");
                            filePath.Append(fileRoot);

                            entry.PrivateKeyFile = new FileInfo(filePath.ToString() + ".pfx");

                            // check for PFX file.
                            if (entry.PrivateKeyFile.Exists)
                            {
                                try
                                {
                                    X509Certificate2 certificate = new X509Certificate2(
                                        entry.PrivateKeyFile.FullName,
                                        new System.Security.SecureString(),
                                        X509KeyStorageFlags.Exportable);

                                    if (certificate.HasPrivateKey)
                                    {
                                        entry.CertificateWithPrivateKey = certificate;
                                    }
                                }
                                catch (System.Security.Cryptography.CryptographicException)
                                {
                                    // Utils.Trace("{1}: {0}", entry.PrivateKeyFile.Name, e.Message.Trim());
                                }
                                catch (Exception e)
                                {
                                    Utils.Trace(e, "Could not load private key certificate from file: {0}", entry.PrivateKeyFile.Name);
                                }
                            }

                            // check for PEM file.
                            else
                            {
                                entry.PrivateKeyFile = new FileInfo(filePath.ToString() + ".pem");

                                if (!entry.PrivateKeyFile.Exists)
                                {
                                    entry.PrivateKeyFile = null;
                                }
                            }
                        }

                        m_certificates[entry.Certificate.Thumbprint] = entry;

                        if (!String.IsNullOrEmpty(thumbprint) && thumbprint == entry.Certificate.Thumbprint)
                        {
                            incompleteSearch = true;
                            break;
                        }
                    }
                    catch (Exception e)
                    {
                        Utils.Trace(e, "Could not load certificate from file: {0}", file.FullName);
                    }
                }

                if (incompleteSearch)
                {
                    m_lastDirectoryCheck = DateTime.MinValue;
                }

                return(m_certificates);
            }
        }
예제 #4
0
        /// <summary>
        /// Validates a certificate.
        /// </summary>
        /// <remarks>
        /// Each UA application may have a list of trusted certificates that is different from
        /// all other UA applications that may be running on the same machine. As a result, the
        /// certificate validator cannot rely completely on the Windows certificate store and
        /// user or machine specific CTLs (certificate trust lists).
        ///
        /// The validator constructs the trust chain for the certificate and follows the chain
        /// until it finds a certification that is in the application trust list. Non-fatal trust
        /// chain errors (i.e. certificate expired) are ignored if the certificate is in the
        /// application trust list.
        ///
        /// If no certificate in the chain is trusted then the validator will still accept the
        /// certification if there are no trust chain errors.
        ///
        /// The validator may be configured to ignore the application trust list and/or trust chain.
        /// </remarks>
        public virtual void Validate(X509Certificate2Collection chain)
        {
            X509Certificate2 certificate = chain[0];

            try
            {
                lock (m_lock)
                {
                    InternalValidate(chain).Wait();

                    // add to list of validated certificates.
                    m_validatedCertificates[certificate.Thumbprint] = certificate;
                }
            }
            catch (AggregateException ae)
            {
                foreach (ServiceResultException e in ae.InnerExceptions)
                {
                    // check for errors that may be suppressed.
                    switch (e.StatusCode)
                    {
                    case StatusCodes.BadCertificateHostNameInvalid:
                    case StatusCodes.BadCertificateIssuerRevocationUnknown:
                    case StatusCodes.BadCertificateIssuerTimeInvalid:
                    case StatusCodes.BadCertificateIssuerUseNotAllowed:
                    case StatusCodes.BadCertificateRevocationUnknown:
                    case StatusCodes.BadCertificateTimeInvalid:
                    case StatusCodes.BadCertificateUriInvalid:
                    case StatusCodes.BadCertificateUseNotAllowed:
                    case StatusCodes.BadCertificateUntrusted:
                    {
                        Utils.Trace("Cert Validate failed: {0}", (StatusCode)e.StatusCode);
                        break;
                    }

                    default:
                    {
                        throw new ServiceResultException(e, StatusCodes.BadCertificateInvalid);
                    }
                    }

                    // invoke callback.
                    bool accept = false;

                    lock (m_callbackLock)
                    {
                        if (m_CertificateValidation != null)
                        {
                            CertificateValidationEventArgs args = new CertificateValidationEventArgs(new ServiceResult(e), certificate);
                            m_CertificateValidation(this, args);
                            accept = args.Accept;
                        }
                    }

                    // throw if rejected.
                    if (!accept)
                    {
                        // write the invalid certificate to a directory if specified.
                        lock (m_lock)
                        {
                            Utils.Trace((int)Utils.TraceMasks.Error, "Certificate '{0}' rejected. Reason={1}", certificate.Subject, (StatusCode)e.StatusCode);

                            if (m_rejectedCertificateStore != null)
                            {
                                Utils.Trace((int)Utils.TraceMasks.Error, "Writing rejected certificate to directory: {0}", m_rejectedCertificateStore);
                                SaveCertificate(certificate);
                            }
                        }

                        throw new ServiceResultException(e, StatusCodes.BadCertificateInvalid);
                    }

                    // add to list of peers.
                    lock (m_lock)
                    {
                        m_validatedCertificates[certificate.Thumbprint] = certificate;
                    }
                    break;
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Loads the private key from a PFX file in the certificate store.
        /// </summary>
        public X509Certificate2 LoadPrivateKey(string thumbprint, string subjectName, System.Security.SecureString password)
        {
            if (m_certificateSubdir == null || !m_certificateSubdir.Exists)
            {
                return(null);
            }

            if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName))
            {
                return(null);
            }

            foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der"))
            {
                try
                {
                    X509Certificate2 certificate = new X509Certificate2(file.FullName);

                    if (!String.IsNullOrEmpty(thumbprint))
                    {
                        if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase))
                        {
                            continue;
                        }
                    }

                    if (!String.IsNullOrEmpty(subjectName))
                    {
                        if (!Utils.CompareDistinguishedName(subjectName, certificate.Subject))
                        {
                            if (subjectName.Contains("=") || !certificate.Subject.Contains("CN=" + subjectName))
                            {
                                continue;
                            }
                        }
                    }

                    string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length);

                    StringBuilder filePath = new StringBuilder();
                    filePath.Append(m_privateKeySubdir.FullName);
                    filePath.Append("\\");
                    filePath.Append(fileRoot);

                    FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx");

                    certificate = new X509Certificate2(
                        privateKeyFile.FullName,
                        (password == null)?new System.Security.SecureString():password,
                        X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);

                    System.Security.Cryptography.RSACryptoServiceProvider rsa = certificate.PrivateKey as System.Security.Cryptography.RSACryptoServiceProvider;

                    if (rsa != null && rsa.CspKeyContainerInfo.Exportable)
                    {
                        int    inputBlockSize = rsa.KeySize / 8 - 42;
                        byte[] bytes1         = rsa.Encrypt(new byte[inputBlockSize], true);
                        byte[] bytes2         = rsa.Decrypt(bytes1, true);

                        if (bytes2 != null)
                        {
                            // Utils.Trace(1, "RSA: {0}", certificate.Thumbprint);
                            return(certificate);
                        }
                    }

                    return(certificate);
                }
                catch (Exception e)
                {
                    Utils.Trace(e, "Could not load private key certificate from file: {0}", file.Name);
                }
            }

            return(null);
        }
예제 #6
0
        /// <summary>
        /// Loads the private key from a PFX file in the certificate store.
        /// </summary>
        public X509Certificate2 LoadPrivateKey(string thumbprint, string subjectName, string password)
        {
            if (m_certificateSubdir == null || !m_certificateSubdir.Exists)
            {
                return(null);
            }

            if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName))
            {
                return(null);
            }

            foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der"))
            {
                try
                {
                    X509Certificate2 certificate = new X509Certificate2(file.FullName);

                    if (!String.IsNullOrEmpty(thumbprint))
                    {
                        if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase))
                        {
                            continue;
                        }
                    }

                    if (!String.IsNullOrEmpty(subjectName))
                    {
                        if (!Utils.CompareDistinguishedName(subjectName, certificate.Subject))
                        {
                            if (subjectName.Contains("=") || !certificate.Subject.Contains("CN=" + subjectName))
                            {
                                continue;
                            }
                        }
                    }

                    string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length);

                    StringBuilder filePath = new StringBuilder();
                    filePath.Append(m_privateKeySubdir.FullName);
                    filePath.Append(Path.DirectorySeparatorChar);
                    filePath.Append(fileRoot);

                    FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx");
                    RSA      rsa            = null;

                    try
                    {
                        certificate = new X509Certificate2(
                            privateKeyFile.FullName,
                            (password == null) ? String.Empty : password,
                            X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet);
                        rsa = certificate.GetRSAPrivateKey();
                    }
                    catch (Exception)
                    {
                        certificate = new X509Certificate2(
                            privateKeyFile.FullName,
                            (password == null) ? String.Empty : password,
                            X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
                        rsa = certificate.GetRSAPrivateKey();
                    }
                    if (rsa != null)
                    {
                        int    inputBlockSize = rsa.KeySize / 8 - 42;
                        byte[] bytes1         = rsa.Encrypt(new byte[inputBlockSize], RSAEncryptionPadding.OaepSHA1);
                        byte[] bytes2         = rsa.Decrypt(bytes1, RSAEncryptionPadding.OaepSHA1);
                        if (bytes2 != null)
                        {
                            // Utils.Trace(1, "RSA: {0}", certificate.Thumbprint);
                            return(certificate);
                        }
                    }
                }
                catch (Exception e)
                {
                    Utils.Trace(e, "Could not load private key for certificate " + subjectName);
                }
            }

            return(null);
        }
        /// <summary>
        /// Loads the private key from a PFX file in the certificate store.
        /// </summary>
        public X509Certificate2 LoadPrivateKey(string thumbprint, string subjectName, string password)
        {
            if (m_certificateSubdir == null || !m_certificateSubdir.Exists)
            {
                return(null);
            }

            if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName))
            {
                return(null);
            }

            foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der"))
            {
                try
                {
                    X509Certificate2 certificate = new X509Certificate2(file.FullName);

                    if (!String.IsNullOrEmpty(thumbprint))
                    {
                        if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase))
                        {
                            continue;
                        }
                    }

                    if (!String.IsNullOrEmpty(subjectName))
                    {
                        if (!X509Utils.CompareDistinguishedName(subjectName, certificate.Subject))
                        {
                            if (subjectName.Contains("="))
                            {
                                continue;
                            }

                            if (!X509Utils.ParseDistinguishedName(certificate.Subject).Any(s => s.Equals("CN=" + subjectName, StringComparison.OrdinalIgnoreCase)))
                            {
                                continue;
                            }
                        }
                    }

                    string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length);

                    StringBuilder filePath = new StringBuilder();
                    filePath.Append(m_privateKeySubdir.FullName);
                    filePath.Append(Path.DirectorySeparatorChar);
                    filePath.Append(fileRoot);

                    X509KeyStorageFlags[] storageFlags =
                    {
                        X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet,
                        X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet
                    };

                    FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx");
                    password = password ?? String.Empty;
                    foreach (var flag in storageFlags)
                    {
                        try
                        {
                            certificate = new X509Certificate2(
                                privateKeyFile.FullName,
                                password,
                                flag);
                            if (X509Utils.VerifyRSAKeyPair(certificate, certificate, true))
                            {
                                return(certificate);
                            }
                        }
                        catch (Exception)
                        {
                            certificate?.Dispose();
                            certificate = null;
                        }
                    }
                }
                catch (Exception e)
                {
                    Utils.Trace(e, "Could not load private key for certificate " + subjectName);
                }
            }

            return(null);
        }
예제 #8
0
        /// <summary>
        /// Gets the application access rules implied by the access rights to the file.
        /// </summary>
        public static void SetAccessRules(String filePath, IList <ApplicationAccessRule> accessRules, bool replaceExisting)
        {
            // get the current permissions from the file or directory.
            FileSystemSecurity security = null;

            FileInfo      fileInfo      = new FileInfo(filePath);
            DirectoryInfo directoryInfo = null;

            if (!fileInfo.Exists)
            {
                directoryInfo = new DirectoryInfo(filePath);

                if (!directoryInfo.Exists)
                {
                    throw new FileNotFoundException("File or directory does not exist.", filePath);
                }

                security = directoryInfo.GetAccessControl(AccessControlSections.Access);
            }
            else
            {
                security = fileInfo.GetAccessControl(AccessControlSections.Access);
            }

            if (replaceExisting)
            {
                // can't use inhieritance when setting permissions
                security.SetAccessRuleProtection(true, false);

                // remove all existing access rules.
                AuthorizationRuleCollection authorizationRules = security.GetAccessRules(true, true, typeof(NTAccount));

                for (int ii = 0; ii < authorizationRules.Count; ii++)
                {
                    FileSystemAccessRule accessRule = authorizationRules[ii] as FileSystemAccessRule;

                    // only care about file system rules.
                    if (accessRule == null)
                    {
                        continue;
                    }

                    security.RemoveAccessRule(accessRule);
                }
            }

            // allow children to inherit rules for directories.
            InheritanceFlags flags = InheritanceFlags.None;

            if (directoryInfo != null)
            {
                flags = InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit;
            }

            // add the new rules.
            for (int ii = 0; ii < accessRules.Count; ii++)
            {
                ApplicationAccessRule applicationRule = accessRules[ii];

                IdentityReference identityReference = applicationRule.IdentityReference;

                if (identityReference == null)
                {
                    if (applicationRule.IdentityName.StartsWith("S-"))
                    {
                        SecurityIdentifier sid = new SecurityIdentifier(applicationRule.IdentityName);

                        if (!sid.IsValidTargetType(typeof(NTAccount)))
                        {
                            continue;
                        }

                        identityReference = sid.Translate(typeof(NTAccount));
                    }
                    else
                    {
                        identityReference = new NTAccount(applicationRule.IdentityName);
                    }
                }

                FileSystemAccessRule fileRule = null;

                switch (applicationRule.Right)
                {
                case ApplicationAccessRight.Run:
                {
                    fileRule = new FileSystemAccessRule(
                        identityReference,
                        (applicationRule.RuleType == AccessControlType.Allow) ? Read : Configure,
                        flags,
                        PropagationFlags.None,
                        ApplicationAccessRule.Convert(applicationRule.RuleType));

                    break;
                }

                case ApplicationAccessRight.Update:
                {
                    fileRule = new FileSystemAccessRule(
                        identityReference,
                        (applicationRule.RuleType == AccessControlType.Allow) ? Update : ConfigureOnly | UpdateOnly,
                        flags,
                        PropagationFlags.None,
                        ApplicationAccessRule.Convert(applicationRule.RuleType));

                    security.SetAccessRule(fileRule);
                    break;
                }

                case ApplicationAccessRight.Configure:
                {
                    fileRule = new FileSystemAccessRule(
                        identityReference,
                        (applicationRule.RuleType == AccessControlType.Allow) ? Configure : ConfigureOnly,
                        flags,
                        PropagationFlags.None,
                        ApplicationAccessRule.Convert(applicationRule.RuleType));

                    break;
                }
                }

                try
                {
                    security.SetAccessRule(fileRule);
                }
                catch (Exception e)
                {
                    Utils.Trace(
                        "Could not set access rule for account '{0}' on file '{1}'. Error={2}",
                        applicationRule.IdentityName,
                        filePath,
                        e.Message);
                }
            }

            if (directoryInfo != null)
            {
                directoryInfo.SetAccessControl((DirectorySecurity)security);
                return;
            }

            fileInfo.SetAccessControl((FileSecurity)security);
        }
예제 #9
0
        /// <summary>
        /// Creates a self signed application instance certificate.
        /// </summary>
        /// <param name="storeType">Type of certificate store (Directory) <see cref="CertificateStoreType"/>.</param>
        /// <param name="storePath">The store path (syntax depends on storeType).</param>
        /// <param name="password">The password to use to protect the certificate.</param>
        /// <param name="applicationUri">The application uri (created if not specified).</param>
        /// <param name="applicationName">Name of the application (optional if subjectName is specified).</param>
        /// <param name="subjectName">The subject used to create the certificate (optional if applicationName is specified).</param>
        /// <param name="domainNames">The domain names that can be used to access the server machine (defaults to local computer name if not specified).</param>
        /// <param name="keySize">Size of the key (1024, 2048 or 4096).</param>
        /// <param name="startTime">The start time.</param>
        /// <param name="lifetimeInMonths">The lifetime of the key in months.</param>
        /// <param name="hashSizeInBits">The hash size in bits.</param>
        /// <param name="isCA">if set to <c>true</c> then a CA certificate is created.</param>
        /// <param name="issuerCAKeyCert">The CA cert with the CA private key.</param>
        /// <returns>The certificate with a private key.</returns>
        public static X509Certificate2 CreateCertificate(
            string storeType,
            string storePath,
            string password,
            string applicationUri,
            string applicationName,
            string subjectName,
            IList <String> domainNames,
            ushort keySize,
            DateTime startTime,
            ushort lifetimeInMonths,
            ushort hashSizeInBits,
            bool isCA,
            X509Certificate2 issuerCAKeyCert)
        {
            if (issuerCAKeyCert != null)
            {
                if (!issuerCAKeyCert.HasPrivateKey)
                {
                    throw new NotSupportedException("Cannot sign with a CA certificate without a private key.");
                }

                throw new NotSupportedException("Signing with an issuer CA certificate is currently unsupported.");
            }

            // set default values.
            SetSuitableDefaults(
                ref applicationUri,
                ref applicationName,
                ref subjectName,
                ref domainNames,
                ref keySize,
                ref lifetimeInMonths,
                isCA);

            // cert generators
            SecureRandom random           = new SecureRandom();
            X509V3CertificateGenerator cg = new X509V3CertificateGenerator();

            // Serial Number
            BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);

            cg.SetSerialNumber(serialNumber);

            // build name attributes
            var nameOids = new ArrayList();

            nameOids.Add(X509Name.DC);
            nameOids.Add(X509Name.CN);

            var nameValues = new ArrayList();

            nameValues.Add(domainNames[0]);
            nameValues.Add(subjectName);

            // self signed
            X509Name subjectDN = new X509Name(nameOids, nameValues);
            X509Name issuerDN  = subjectDN;

            cg.SetIssuerDN(issuerDN);
            cg.SetSubjectDN(subjectDN);

            // valid for
            cg.SetNotBefore(startTime);
            cg.SetNotAfter(startTime.AddMonths(lifetimeInMonths));

            // Private/Public Key
            AsymmetricCipherKeyPair subjectKeyPair;
            var keyGenerationParameters = new KeyGenerationParameters(random, keySize);
            var keyPairGenerator        = new RsaKeyPairGenerator();

            keyPairGenerator.Init(keyGenerationParameters);
            subjectKeyPair = keyPairGenerator.GenerateKeyPair();
            cg.SetPublicKey(subjectKeyPair.Public);

            // add extensions
            // Subject key identifier
            cg.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false,
                            new SubjectKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public)));

            // Basic constraints
            cg.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(isCA));

            // Authority Key identifier
            var issuerKeyPair      = subjectKeyPair;
            var issuerSerialNumber = serialNumber;

            cg.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false,
                            new AuthorityKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public),
                                                       new GeneralNames(new GeneralName(issuerDN)), issuerSerialNumber));

            if (!isCA)
            {
                // Key usage
                cg.AddExtension(X509Extensions.KeyUsage, true,
                                new KeyUsage(KeyUsage.DataEncipherment | KeyUsage.DigitalSignature |
                                             KeyUsage.NonRepudiation | KeyUsage.KeyCertSign | KeyUsage.KeyEncipherment));

                // Extended Key usage
                cg.AddExtension(X509Extensions.ExtendedKeyUsage, true,
                                new ExtendedKeyUsage(new List <DerObjectIdentifier>()
                {
                    new DerObjectIdentifier("1.3.6.1.5.5.7.3.1"), // server auth
                    new DerObjectIdentifier("1.3.6.1.5.5.7.3.2"), // client auth
                }));

                // subject alternate name
                cg.AddExtension(X509Extensions.SubjectAlternativeName, false,
                                new GeneralNames(new GeneralName[] {
                    new GeneralName(GeneralName.UniformResourceIdentifier, applicationUri),
                    new GeneralName(GeneralName.DnsName, domainNames[0])
                }));
            }
            else
            {
                // Key usage CA
                cg.AddExtension(X509Extensions.KeyUsage, true,
                                new KeyUsage(KeyUsage.CrlSign | KeyUsage.DigitalSignature | KeyUsage.KeyCertSign));
            }

            // sign certificate
            ISignatureFactory signatureFactory =
                new Asn1SignatureFactory((hashSizeInBits < 256) ? "SHA1WITHRSA" : "SHA256WITHRSA", subjectKeyPair.Private, random);

            Org.BouncyCastle.X509.X509Certificate x509 = cg.Generate(signatureFactory);

            // create pkcs12 store for cert and private key
            X509Certificate2 certificate = null;

            using (MemoryStream pfxData = new MemoryStream())
            {
                Pkcs12Store            pkcsStore = new Pkcs12StoreBuilder().Build();
                X509CertificateEntry[] chain     = new X509CertificateEntry[1];
                string passcode = "passcode";
                chain[0] = new X509CertificateEntry(x509);
                pkcsStore.SetKeyEntry(applicationName, new AsymmetricKeyEntry(subjectKeyPair.Private), chain);
                pkcsStore.Save(pfxData, passcode.ToCharArray(), random);

                // merge into X509Certificate2
                certificate = CreateCertificateFromPKCS12(pfxData.ToArray(), passcode);
            }

            Utils.Trace(Utils.TraceMasks.Security, "Created new certificate: {0}", certificate.Thumbprint);

            // add cert to the store.
            if (!String.IsNullOrEmpty(storePath))
            {
                ICertificateStore store = null;
                if (storeType == CertificateStoreType.X509Store)
                {
                    store = new X509CertificateStore();
                }
                else if (storeType == CertificateStoreType.Directory)
                {
                    store = new DirectoryCertificateStore();
                }
                else
                {
                    throw new ArgumentException("Invalid store type");
                }

                store.Open(storePath);
                store.Add(certificate);
                store.Close();
                store.Dispose();
            }

            // note: this cert has a private key!
            return(certificate);
        }
예제 #10
0
        /// <summary>
        /// Validates a certificate with domain validation check.
        /// <see cref="Validate(X509Certificate2Collection)"/>
        /// </summary>
        public virtual void Validate(X509Certificate2Collection chain, ConfiguredEndpoint endpoint)
        {
            X509Certificate2 certificate = chain[0];

            try
            {
                lock (m_lock)
                {
                    InternalValidate(chain, endpoint).GetAwaiter().GetResult();

                    // add to list of validated certificates.
                    m_validatedCertificates[certificate.Thumbprint] = new X509Certificate2(certificate.RawData);
                }
            }
            catch (ServiceResultException se)
            {
                // check for errors that may be suppressed.
                if (ContainsUnsuppressibleSC(se.Result))
                {
                    SaveCertificate(certificate);
                    Utils.Trace(Utils.TraceMasks.Error, "Certificate '{0}' rejected. Reason={1}.", certificate.Subject, se.Result.ToString());
                    TraceInnerServiceResults(se.Result);
                    throw new ServiceResultException(se, StatusCodes.BadCertificateInvalid);
                }
                else
                {
                    Utils.Trace("Certificate Vaildation failed for '{0}'. Reason={1}", certificate.Subject, se.ToLongString());
                    TraceInnerServiceResults(se.Result);
                }

                // invoke callback.
                bool accept = false;

                ServiceResult serviceResult = se.Result;
                lock (m_callbackLock)
                {
                    if (m_CertificateValidation != null)
                    {
                        do
                        {
                            CertificateValidationEventArgs args = new CertificateValidationEventArgs(serviceResult, certificate);
                            m_CertificateValidation(this, args);
                            if (args.AcceptAll)
                            {
                                accept        = true;
                                serviceResult = null;
                                break;
                            }
                            accept = args.Accept;
                            if (accept)
                            {
                                serviceResult = serviceResult.InnerResult;
                            }
                            else
                            {
                                // report the rejected service result
                                se = new ServiceResultException(serviceResult);
                            }
                        } while (accept && serviceResult != null);
                    }
                }

                // throw if rejected.
                if (!accept)
                {
                    // write the invalid certificate to rejected store if specified.
                    Utils.Trace(Utils.TraceMasks.Error, "Certificate '{0}' rejected. Reason={1}",
                                certificate.Subject, serviceResult != null ? serviceResult.ToString() : "Unknown Error");
                    SaveCertificate(certificate);

                    throw new ServiceResultException(se, StatusCodes.BadCertificateInvalid);
                }

                // add to list of peers.
                lock (m_lock)
                {
                    Utils.Trace("Validation error suppressed for '{0}'.", certificate.Subject);
                    m_validatedCertificates[certificate.Thumbprint] = new X509Certificate2(certificate.RawData);
                }
            }
        }