Beispiel #1
0
        /// <summary>
        /// Creates a new endpoint from a url that is not part of the collection.
        /// </summary>
        /// <remarks>
        /// Call the Add() method to add it to the collection.
        /// </remarks>
        public ConfiguredEndpoint Create(string url)
        {
            // check for security parameters appended to the URL
            string parameters = null;

            int index = url.IndexOf("- [", StringComparison.Ordinal);

            if (index != -1)
            {
                parameters = url.Substring(index + 3);
                url        = url.Substring(0, index).Trim();
            }

            MessageSecurityMode securityMode = MessageSecurityMode.SignAndEncrypt;
            string securityPolicyUri         = SecurityPolicies.Basic128Rsa15;
            bool   useBinaryEncoding         = true;

            if (!String.IsNullOrEmpty(parameters))
            {
                string[] fields = parameters.Split(new char[] { '-', '[', ':', ']' }, StringSplitOptions.RemoveEmptyEntries);

                try
                {
                    if (fields.Length > 0)
                    {
                        securityMode = (MessageSecurityMode)Enum.Parse(typeof(MessageSecurityMode), fields[0], false);
                    }
                    else
                    {
                        securityMode = MessageSecurityMode.None;
                    }
                }
                catch
                {
                    securityMode = MessageSecurityMode.None;
                }

                try
                {
                    if (fields.Length > 1)
                    {
                        securityPolicyUri = SecurityPolicies.GetUri(fields[1]);
                    }
                    else
                    {
                        securityPolicyUri = SecurityPolicies.None;
                    }
                }
                catch
                {
                    securityPolicyUri = SecurityPolicies.None;
                }

                try
                {
                    if (fields.Length > 2)
                    {
                        useBinaryEncoding = fields[2] == "Binary";
                    }
                    else
                    {
                        useBinaryEncoding = false;
                    }
                }
                catch
                {
                    useBinaryEncoding = false;
                }
            }

            Uri uri = new Uri(url);

            EndpointDescription description = new EndpointDescription();

            description.EndpointUrl            = uri.ToString();
            description.SecurityMode           = securityMode;
            description.SecurityPolicyUri      = securityPolicyUri;
            description.Server.ApplicationUri  = Utils.UpdateInstanceUri(uri.ToString());
            description.Server.ApplicationName = uri.AbsolutePath;

            if (description.EndpointUrl.StartsWith(Utils.UriSchemeOpcTcp, StringComparison.Ordinal))
            {
                description.TransportProfileUri = Profiles.UaTcpTransport;
                description.Server.DiscoveryUrls.Add(description.EndpointUrl);
            }
            else if (description.EndpointUrl.StartsWith(Utils.UriSchemeHttps, StringComparison.Ordinal))
            {
                description.TransportProfileUri = Profiles.HttpsBinaryTransport;
                description.Server.DiscoveryUrls.Add(description.EndpointUrl);
            }

            ConfiguredEndpoint endpoint = new ConfiguredEndpoint(this, description, null);

            endpoint.Configuration.UseBinaryEncoding = useBinaryEncoding;
            endpoint.UpdateBeforeConnect             = true;
            return(endpoint);
        }
Beispiel #2
0
 /// <summary>
 /// Adds a previous created endpoint to the collection.
 /// </summary>
 public void Add(ConfiguredEndpoint item)
 {
     Insert(item, -1);
 }
Beispiel #3
0
        /// <summary>
        /// Throws an exception if validation fails.
        /// </summary>
        /// <param name="certificates">The certificates to be checked.</param>
        /// <param name="endpoint">The endpoint for domain validation.</param>
        /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception>
        protected virtual async Task InternalValidate(X509Certificate2Collection certificates, ConfiguredEndpoint endpoint)
        {
            X509Certificate2 certificate = certificates[0];

            // check for previously validated certificate.
            X509Certificate2 certificate2 = null;

            if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2))
            {
                if (Utils.IsEqual(certificate2.RawData, certificate.RawData))
                {
                    return;
                }
            }

            CertificateIdentifier trustedCertificate = await GetTrustedCertificate(certificate);

            // get the issuers (checks the revocation lists if using directory stores).
            List <CertificateIdentifier> issuers = new List <CertificateIdentifier>();
            bool isIssuerTrusted = await GetIssuers(certificates, issuers);

            // setup policy chain
            X509ChainPolicy policy = new X509ChainPolicy();

            policy.RevocationFlag    = X509RevocationFlag.EntireChain;
            policy.RevocationMode    = X509RevocationMode.NoCheck;
            policy.VerificationFlags = X509VerificationFlags.NoFlag;

            foreach (CertificateIdentifier issuer in issuers)
            {
                if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0)
                {
                    policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown;
                    policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown;
                    policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown;
                    policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown;
                }

                // we did the revocation check in the GetIssuers call. No need here.
                policy.RevocationMode = X509RevocationMode.NoCheck;
                policy.ExtraStore.Add(issuer.Certificate);
            }

            // build chain.
            X509Chain chain = new X509Chain();

            chain.ChainPolicy = policy;
            chain.Build(certificate);

            // check the chain results.
            CertificateIdentifier target = trustedCertificate;

            if (target == null)
            {
                target = new CertificateIdentifier(certificate);
            }

            ServiceResult sresult = null;

            for (int ii = 0; ii < chain.ChainElements.Count; ii++)
            {
                X509ChainElement element = chain.ChainElements[ii];

                CertificateIdentifier issuer = null;

                if (ii < issuers.Count)
                {
                    issuer = issuers[ii];
                }
                // check for chain status errors.
                if (element.ChainElementStatus.Length > 0)
                {
                    foreach (X509ChainStatus status in element.ChainElementStatus)
                    {
                        ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0));
                        if (ServiceResult.IsBad(result))
                        {
                            sresult = new ServiceResult(result, sresult);
                        }
                    }
                }

                if (issuer != null)
                {
                    target = issuer;
                }
            }

            // check whether the chain is complete (if there is a chain)
            bool issuedByCA      = !X509Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer);
            bool chainIncomplete = false;

            if (issuers.Count > 0)
            {
                var rootCertificate = issuers[issuers.Count - 1].Certificate;
                if (!X509Utils.CompareDistinguishedName(rootCertificate.Subject, rootCertificate.Issuer))
                {
                    chainIncomplete = true;
                }
            }
            else
            {
                if (issuedByCA)
                {
                    // no issuer found at all
                    chainIncomplete = true;
                }
            }

            // check if certificate issuer is trusted.
            if (issuedByCA && !isIssuerTrusted && trustedCertificate == null)
            {
                var message = CertificateMessage("Certificate Issuer is not trusted.", certificate);
                sresult = new ServiceResult(StatusCodes.BadCertificateUntrusted,
                                            null, null, message, null, sresult);
            }

            // check if certificate is trusted.
            if (trustedCertificate == null && !isIssuerTrusted)
            {
                if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData))
                {
                    var message = CertificateMessage("Certificate is not trusted.", certificate);
                    sresult = new ServiceResult(StatusCodes.BadCertificateUntrusted,
                                                null, null, message, null, sresult);
                }
            }

            if (endpoint != null && !FindDomain(certificate, endpoint))
            {
                string message = Utils.Format(
                    "The domain '{0}' is not listed in the server certificate.",
                    endpoint.EndpointUrl.DnsSafeHost);
                sresult = new ServiceResult(StatusCodes.BadCertificateHostNameInvalid,
                                            null, null, message, null, sresult
                                            );
            }

            // check if certificate is valid for use as app/sw or user cert
            X509KeyUsageFlags certificateKeyUsage = X509Utils.GetKeyUsage(certificate);

            if ((certificateKeyUsage & X509KeyUsageFlags.DataEncipherment) == 0)
            {
                sresult = new ServiceResult(StatusCodes.BadCertificateUseNotAllowed,
                                            null, null, "Usage of certificate is not allowed.", null, sresult);
            }

            // check if minimum requirements are met
            if (m_rejectSHA1SignedCertificates && IsSHA1SignatureAlgorithm(certificate.SignatureAlgorithm))
            {
                sresult = new ServiceResult(StatusCodes.BadCertificatePolicyCheckFailed,
                                            null, null, "SHA1 signed certificates are not trusted.", null, sresult);
            }

            int keySize = X509Utils.GetRSAPublicKeySize(certificate);

            if (keySize < m_minimumCertificateKeySize)
            {
                sresult = new ServiceResult(StatusCodes.BadCertificatePolicyCheckFailed,
                                            null, null, "Certificate doesn't meet minimum key length requirement.", null, sresult);
            }

            if (issuedByCA && chainIncomplete)
            {
                var message = CertificateMessage("Certificate chain validation incomplete.", certificate);
                sresult = new ServiceResult(StatusCodes.BadCertificateChainIncomplete,
                                            null, null, message, null, sresult);
            }
            if (sresult != null)
            {
                throw new ServiceResultException(sresult);
            }
        }
Beispiel #4
0
 /// <summary>
 /// Inserts an item to the <see cref="T:System.Collections.Generic.IList`1"/> at the specified index.
 /// </summary>
 /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
 /// <param name="item">The object to insert into the <see cref="T:System.Collections.Generic.IList`1"/>.</param>
 /// <exception cref="T:System.ArgumentOutOfRangeException">
 ///     <paramref name="index"/> is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"/>.</exception>
 /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"/> is read-only.</exception>
 public void Insert(int index, ConfiguredEndpoint item)
 {
     Insert(item, index);
 }
Beispiel #5
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.ToString());
                    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);
                }
            }
        }