Creates a manages certificates.
        /// <summary>
        /// Initializes the object with a UA identity token
        /// </summary>
        private void Initialize(UserIdentityToken token)
        {
            if (token == null)
            {
                throw new ArgumentNullException(nameof(token));
            }
            m_grantedRoleIds = new NodeIdCollection();
            m_token          = token;

            UserNameIdentityToken usernameToken = token as UserNameIdentityToken;

            if (usernameToken != null)
            {
                m_tokenType       = UserTokenType.UserName;
                m_issuedTokenType = null;
                m_displayName     = usernameToken.UserName;
                return;
            }

            X509IdentityToken x509Token = token as X509IdentityToken;

            if (x509Token != null)
            {
                m_tokenType       = UserTokenType.Certificate;
                m_issuedTokenType = null;
                if (x509Token.Certificate != null)
                {
                    m_displayName = x509Token.Certificate.Subject;
                }
                else
                {
                    X509Certificate2 cert = CertificateFactory.Create(x509Token.CertificateData, true);
                    m_displayName = cert.Subject;
                }
                return;
            }

            IssuedIdentityToken issuedToken = token as IssuedIdentityToken;

            if (issuedToken != null)
            {
                if (issuedToken.IssuedTokenType == Ua.IssuedTokenType.JWT)
                {
                    if (issuedToken.DecryptedTokenData == null || issuedToken.DecryptedTokenData.Length == 0)
                    {
                        throw new ArgumentException("JSON Web Token has no data associated with it.", nameof(token));
                    }

                    m_tokenType       = UserTokenType.IssuedToken;
                    m_issuedTokenType = new XmlQualifiedName("", Opc.Ua.Profiles.JwtUserToken);
                    m_displayName     = "JWT";
                    return;
                }
                else
                {
                    throw new NotSupportedException("Only JWT Issued Tokens are supported!");
                }
            }

            AnonymousIdentityToken anonymousToken = token as AnonymousIdentityToken;

            if (anonymousToken != null)
            {
                m_tokenType       = UserTokenType.Anonymous;
                m_issuedTokenType = null;
                m_displayName     = "Anonymous";
                return;
            }

            throw new ArgumentException("Unrecognized UA user identity token type.", nameof(token));
        }
Exemple #2
0
 /// <summary>
 /// Returns if a self signed certificate is properly signed.
 /// </summary>
 private static bool IsSignatureValid(X509Certificate2 cert)
 {
     return(CertificateFactory.VerifySelfSigned(cert));
 }
        /// <summary>
        /// Throws an exception if validation fails.
        /// </summary>
        /// <param name="certificates">The certificates to be checked.</param>
        /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception>
        protected virtual async Task InternalValidate(X509Certificate2Collection certificates)
        {
            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.
            bool      chainStatusChecked = false;
            X509Chain chain = new X509Chain();

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

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

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

            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))
                        {
                            // check untrusted certificates.
                            if (trustedCertificate == null)
                            {
                                ServiceResult errorResult = new ServiceResult(
                                    result.StatusCode,
                                    result.SymbolicId,
                                    result.NamespaceUri,
                                    result.LocalizedText,
                                    result.AdditionalInfo,
                                    StatusCodes.BadCertificateUntrusted);

                                throw new ServiceResultException(errorResult);
                            }

                            throw new ServiceResultException(result);
                        }

                        chainStatusChecked = true;
                    }
                }
                else
                {
                    chainStatusChecked = true;
                }

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

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

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

            if (issuedByCA && (!chainStatusChecked || chainIncomplete))
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadCertificateChainIncomplete,
                          "Certificate chain validation incomplete.\r\nSubjectName: {0}\r\nIssuerName: {1}",
                          certificate.SubjectName.Name,
                          certificate.IssuerName.Name);
            }

            // check if certificate issuer is trusted.
            if (issuedByCA && !isIssuerTrusted && trustedCertificate == null)
            {
                if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData))
                {
                    throw ServiceResultException.Create(
                              StatusCodes.BadCertificateUntrusted,
                              "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}",
                              certificate.SubjectName.Name,
                              certificate.IssuerName.Name);
                }
            }

            // check if certificate is trusted.
            if (trustedCertificate == null && !isIssuerTrusted)
            {
                if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData))
                {
                    throw ServiceResultException.Create(
                              StatusCodes.BadCertificateUntrusted,
                              "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}",
                              certificate.SubjectName.Name,
                              certificate.IssuerName.Name);
                }
            }

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

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

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

            int keySize = CertificateFactory.GetRSAPublicKeySize(certificate);

            if (keySize < m_minimumCertificateKeySize)
            {
                throw new ServiceResultException(StatusCodes.BadCertificatePolicyCheckFailed, "Certificate doesn't meet minimum key length requirement");
            }
        }
        /// <summary>
        /// Returns the certificate information for a trusted issuer certificate.
        /// </summary>
        private async Task <CertificateIdentifier> GetIssuer(
            X509Certificate2 certificate,
            CertificateIdentifierCollection explicitList,
            CertificateStoreIdentifier certificateStore,
            bool checkRecovationStatus)
        {
            // check if self-signed.
            if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer))
            {
                return(null);
            }

            string subjectName  = certificate.IssuerName.Name;
            string keyId        = null;
            string serialNumber = null;

            // find the authority key identifier.
            X509AuthorityKeyIdentifierExtension authority = FindAuthorityKeyIdentifier(certificate);

            if (authority != null)
            {
                keyId        = authority.KeyId;
                serialNumber = authority.SerialNumber;
            }

            // check in explicit list.
            if (explicitList != null)
            {
                for (int ii = 0; ii < explicitList.Count; ii++)
                {
                    X509Certificate2 issuer = await explicitList[ii].Find(false);

                    if (issuer != null)
                    {
                        if (!IsIssuerAllowed(issuer))
                        {
                            continue;
                        }

                        if (Match(issuer, subjectName, serialNumber, keyId))
                        {
                            // can't check revocation.
                            return(new CertificateIdentifier(issuer, CertificateValidationOptions.SuppressRevocationStatusUnknown));
                        }
                    }
                }
            }

            // check in certificate store.
            if (certificateStore != null)
            {
                ICertificateStore store = certificateStore.OpenStore();

                try
                {
                    X509Certificate2Collection certificates = await store.Enumerate();

                    for (int ii = 0; ii < certificates.Count; ii++)
                    {
                        X509Certificate2 issuer = certificates[ii];

                        if (issuer != null)
                        {
                            if (!IsIssuerAllowed(issuer))
                            {
                                continue;
                            }

                            if (Match(issuer, subjectName, serialNumber, keyId))
                            {
                                CertificateValidationOptions options = certificateStore.ValidationOptions;

                                // already checked revocation for file based stores. windows based stores always suppress.
                                options |= CertificateValidationOptions.SuppressRevocationStatusUnknown;

                                if (checkRecovationStatus)
                                {
                                    StatusCode status = store.IsRevoked(issuer, certificate);

                                    if (StatusCode.IsBad(status) && status != StatusCodes.BadNotSupported)
                                    {
                                        if (status == StatusCodes.BadCertificateRevocationUnknown)
                                        {
                                            if (CertificateFactory.IsCertificateAuthority(certificate))
                                            {
                                                status.Code = StatusCodes.BadCertificateIssuerRevocationUnknown;
                                            }

                                            if (m_rejectUnknownRevocationStatus)
                                            {
                                                throw new ServiceResultException(status);
                                            }
                                        }
                                        else
                                        {
                                            throw new ServiceResultException(status);
                                        }
                                    }
                                }

                                return(new CertificateIdentifier(certificates[ii], options));
                            }
                        }
                    }
                }
                finally
                {
                    store.Close();
                }
            }

            // not a trusted issuer.
            return(null);
        }
Exemple #5
0
        public void InitializeSinglePolicy(
            Type contractType,
            ApplicationConfiguration configuration,
            BindingFactory bindingFactory,
            EndpointConfiguration endpointConfiguration,
            List <EndpointDescription> endpoints,
            MessageSecurityMode securityMode,
            string securityPolicyUri)
        {
            // allow any url to match.
            System.ServiceModel.ServiceBehaviorAttribute behavoir = this.Description.Behaviors.Find <System.ServiceModel.ServiceBehaviorAttribute>();
            behavoir.AddressFilterMode = System.ServiceModel.AddressFilterMode.Any;

            // specify service credentials
            ServiceCredentials credentials = new ServiceCredentials();

            credentials.ClientCertificate.Authentication.CertificateValidationMode  = X509CertificateValidationMode.Custom;
            credentials.ClientCertificate.Authentication.TrustedStoreLocation       = StoreLocation.LocalMachine;
            credentials.ClientCertificate.Authentication.RevocationMode             = X509RevocationMode.NoCheck;
            credentials.ClientCertificate.Authentication.CustomCertificateValidator = configuration.CertificateValidator.GetChannelValidator();

            if (configuration.SecurityConfiguration.ApplicationCertificate != null)
            {
                X509Certificate2 certificate = configuration.SecurityConfiguration.ApplicationCertificate.Find(true);

                if (certificate != null)
                {
                    credentials.ServiceCertificate.Certificate = CertificateFactory.Load(certificate, true);
                }
            }

            this.Description.Behaviors.Add(credentials);

            // check if explicitly specified.
            ServiceThrottlingBehavior throttle = this.Description.Behaviors.Find <ServiceThrottlingBehavior>();

            if (throttle == null)
            {
                throttle = new ServiceThrottlingBehavior();

                throttle.MaxConcurrentCalls     = 1000;
                throttle.MaxConcurrentInstances = 100;
                throttle.MaxConcurrentSessions  = 100;

                this.Description.Behaviors.Add(throttle);
            }

            // add the endpoints for each base address.
            foreach (Uri baseAddress in this.BaseAddresses)
            {
                ServiceEndpoint endpoint = null;

                // find endpoint configuration.
                EndpointDescription description = null;

                foreach (EndpointDescription current in endpoints)
                {
                    if (new Uri(current.EndpointUrl) == baseAddress)
                    {
                        description = current;
                        break;
                    }
                }

                // skip endpoints without a matching base address.
                if (description == null)
                {
                    continue;
                }

                // set the supported profiles.
                description.TransportProfileUri = Profiles.WsHttpXmlOrBinaryTransport;

                // create the SOAP XML binding
                Binding binding = bindingFactory.Create(baseAddress.Scheme, description, endpointConfiguration);

                // add the session endpoint.
                endpoint = this.AddServiceEndpoint(contractType, binding, baseAddress, baseAddress);

                // set the protection level
                if (securityMode == MessageSecurityMode.Sign)
                {
                    endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;
                }

                // update the max items in graph (set to an low value by default).
                foreach (OperationDescription operation in endpoint.Contract.Operations)
                {
                    operation.Behaviors.Find <DataContractSerializerOperationBehavior>().MaxItemsInObjectGraph = Int32.MaxValue;
                }
            }
        }
Exemple #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("="))
                            {
                                continue;
                            }

                            if (!Utils.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);

                    FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx");
                    password = password ?? String.Empty;
                    try
                    {
                        certificate = new X509Certificate2(
                            privateKeyFile.FullName,
                            password,
                            X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet);
                        if (CertificateFactory.VerifyRSAKeyPair(certificate, certificate, true))
                        {
                            return(certificate);
                        }
                    }
                    catch (Exception)
                    {
                        certificate = new X509Certificate2(
                            privateKeyFile.FullName,
                            password,
                            X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
                        if (CertificateFactory.VerifyRSAKeyPair(certificate, certificate, true))
                        {
                            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/PEM file in the certificate store.
        /// </summary>
        public async Task <X509Certificate2> LoadPrivateKey(string thumbprint, string subjectName, string password)
        {
            if (NoPrivateKeys || m_certificateSubdir == null || !m_certificateSubdir.Exists)
            {
                return(null);
            }

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

            // on some platforms, specifically in virtualized environments,
            // reloading a previously created and saved private key may fail on the first attempt.
            const int retryDelay   = 100;
            int       retryCounter = 3;

            while (retryCounter-- > 0)
            {
                bool      certificateFound = false;
                Exception importException  = 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.OrdinalIgnoreCase))
                            {
                                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.Ordinal)))
                                {
                                    continue;
                                }
                            }
                        }

                        // skip if not RSA certificate
                        if (X509Utils.GetRSAPublicKeySize(certificate) < 0)
                        {
                            continue;
                        }

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

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

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

                        FileInfo privateKeyFilePfx = new FileInfo(filePath + ".pfx");
                        FileInfo privateKeyFilePem = new FileInfo(filePath + ".pem");
                        password = password ?? String.Empty;
                        if (privateKeyFilePfx.Exists)
                        {
                            certificateFound = true;
                            foreach (var flag in storageFlags)
                            {
                                try
                                {
                                    certificate = new X509Certificate2(
                                        privateKeyFilePfx.FullName,
                                        password,
                                        flag);
                                    if (X509Utils.VerifyRSAKeyPair(certificate, certificate, true))
                                    {
                                        Utils.LogInfo(Utils.TraceMasks.Security, "Imported the PFX private key for [{0}].", certificate.Thumbprint);
                                        return(certificate);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    importException = ex;
                                    certificate?.Dispose();
                                }
                            }
                        }
                        // if PFX file doesn't exist, check for PEM file.
                        else if (privateKeyFilePem.Exists)
                        {
                            certificateFound = true;
                            try
                            {
                                byte[] pemDataBlob = File.ReadAllBytes(privateKeyFilePem.FullName);
                                certificate = CertificateFactory.CreateCertificateWithPEMPrivateKey(certificate, pemDataBlob, password);
                                if (X509Utils.VerifyRSAKeyPair(certificate, certificate, true))
                                {
                                    Utils.LogInfo(Utils.TraceMasks.Security, "Imported the PEM private key for [{0}].", certificate.Thumbprint);
                                    return(certificate);
                                }
                            }
                            catch (Exception exception)
                            {
                                certificate?.Dispose();
                                importException = exception;
                            }
                        }
                        else
                        {
                            Utils.LogError(Utils.TraceMasks.Security, "A private key for the certificate with thumbprint [{0}] does not exist.", certificate.Thumbprint);
                            continue;
                        }
                    }
                    catch (Exception e)
                    {
                        Utils.LogError(e, "Could not load private key for certificate {0}", subjectName);
                    }
                }

                // found a certificate, but some error occurred
                if (certificateFound)
                {
                    Utils.LogError(Utils.TraceMasks.Security, "The private key for the certificate with subject {0} failed to import.", subjectName);
                    if (importException != null)
                    {
                        Utils.LogError(importException, "Certificate import failed.");
                    }
                }
                else
                {
                    if (!String.IsNullOrEmpty(thumbprint))
                    {
                        Utils.LogError(Utils.TraceMasks.Security, "A Private key for the certificate with thumbpint {0} was not found.", thumbprint);
                    }
                    // if no private key was found, no need to retry
                    break;
                }

                // retry within a few ms
                if (retryCounter > 0)
                {
                    Utils.LogInfo(Utils.TraceMasks.Security, "Retry to import private key after {0} ms.", retryDelay);
                    await Task.Delay(retryDelay).ConfigureAwait(false);
                }
            }

            return(null);
        }