/// <summary> /// Formats the numeric range as a string. /// </summary> /// <param name="format">(Unused) Always pass null</param> /// <param name="formatProvider">(Unused) Always pass null</param> /// <exception cref="FormatException">Thrown if non-null parameters are passed</exception> public string ToString(string format, IFormatProvider formatProvider) { if (format == null) { StringBuilder path = new StringBuilder(); // write the reference type component. switch (m_elementType) { case ElementType.AnyHierarchical: { path.Append('/'); break; } case ElementType.AnyComponent: { path.Append('.'); break; } case ElementType.ForwardReference: case ElementType.InverseReference: { if (m_referenceTypeName != null && !String.IsNullOrEmpty(m_referenceTypeName.Name)) { path.Append('<'); if (!m_includeSubtypes) { path.Append('#'); } if (m_elementType == ElementType.InverseReference) { path.Append('!'); } if (m_referenceTypeName.NamespaceIndex != 0) { path.AppendFormat("{0}:", m_referenceTypeName.NamespaceIndex); } EncodeName(path, m_referenceTypeName.Name); path.Append('>'); } break; } } // write the target browse name component. if (m_targetName != null && !String.IsNullOrEmpty(m_targetName.Name)) { if (m_targetName.NamespaceIndex != 0) { path.AppendFormat("{0}:", m_targetName.NamespaceIndex); } EncodeName(path, m_targetName.Name); } return(path.ToString()); } throw new FormatException(Utils.Format("Invalid format string: '{0}'.", format)); }
/// <summary> /// Extracts a browse name with an optional namespace prefix from the reader. /// </summary> private static QualifiedName ParseName( StringReader reader, bool referenceName) { ushort namespaceIndex = 0; // extract namespace index if present. StringBuilder buffer = new StringBuilder(); int last = reader.Peek(); for (int next = last; next != -1; next = reader.Peek()) { last = next; if (!Char.IsDigit((char)next)) { if (next == ':') { reader.Read(); namespaceIndex = Convert.ToUInt16(buffer.ToString(), CultureInfo.InvariantCulture); buffer.Length = 0; // fetch next character. last = reader.Peek(); } break; } buffer.Append((char)next); reader.Read(); } // extract rest of name. for (int next = last; next != -1; next = reader.Peek()) { last = next; // check for terminator. if (referenceName) { if (next == '>') { reader.Read(); break; } } else { if (next == '<' || next == '/' || next == '.') { break; } } // check for invalid character. if (next == '!' || next == ':' || next == '<' || next == '>' || next == '/' || next == '.') { throw new ServiceResultException( StatusCodes.BadSyntaxError, Utils.Format("Unexpected character '{0}' in browse path.", next)); } // check for escape character. if (next == '&') { next = reader.Read(); next = reader.Read(); buffer.Append((char)next); continue; } // append character. buffer.Append((char)next); reader.Read(); } // check for enclosing bracket. if (referenceName) { if (last != '>') { throw new ServiceResultException( StatusCodes.BadSyntaxError, Utils.Format("Missing file '>' for reference type name in browse path.")); } } if (buffer.Length == 0) { if (referenceName) { throw new ServiceResultException( StatusCodes.BadSyntaxError, Utils.Format("Reference type name is null in browse path.")); } if (namespaceIndex == 0) { return(null); } } return(new QualifiedName(buffer.ToString(), namespaceIndex)); }
/// <summary> /// Recursively collects the nodes within a type hierarchy. /// </summary> private void GetInstanceHierarchyForType( HierarchyBrowsePath parent, ExpandedNodeId instanceId, InstanceDeclarationHierarchy hierarchy) { // the instance must be local to the address space. ILocalNode instance = m_nodes.Find(instanceId) as ILocalNode; if (instance == null) { return; } // must be an object, variable or method. if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0) { return; } // get the naming rule. NamingRule namingRule = GetNamingRule(instance.ModellingRule); // only include instances with unique browse names in the hierarchy. if (namingRule != NamingRule.Unique && namingRule != NamingRule.UniqueOptional) { return; } // construct the browse path that identifies the node. string browsePath = null; if (parent.BrowsePath == "/") { browsePath = Utils.Format("/{0}", instance.BrowseName); } else { browsePath = Utils.Format("{0}/{1}", parent.BrowsePath, instance.BrowseName); } // check if the browse path already exists in the hierarchy. HierarchyBrowsePath child = null; if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out child)) { child = new HierarchyBrowsePath(); child.BrowsePath = browsePath; child.DeclarationId = instance.NodeId; child.InstanceId = null; child.IsModelParent = false; child.IsOptional = namingRule != NamingRule.Unique; // add new browse path to hierarchy. hierarchy.BrowsePaths.Add(browsePath, child); } // override any declaration specified in a supertype. child.DeclarationId = instance.NodeId; // check if node has been processed via another path. HierarchyBrowsePath alternatePath = null; if (hierarchy.Declarations.TryGetValue(instance.NodeId, out alternatePath)) { // keep the model parent path as the primary path. if (!alternatePath.IsModelParent && child.IsModelParent) { hierarchy.Declarations[instance.NodeId] = child; } // nothing more to do since node has been processed once. return; } // save child. hierarchy.Declarations.Add(instance.NodeId, child); // follow children. foreach (IReference reference in instance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree)) { GetInstanceHierarchyForType(child, reference.TargetId, hierarchy); } }
/// <summary> /// Dispatches an incoming binary encoded request. /// </summary> /// <param name="incoming">Incoming request.</param> public virtual IServiceResponse ProcessRequest(IServiceRequest incoming) { try { SetRequestContext(RequestEncoding.Binary); ServiceDefinition service = null; // find service. if (!SupportedServices.TryGetValue(incoming.TypeId, out service)) { throw new ServiceResultException(StatusCodes.BadServiceUnsupported, Utils.Format("'{0}' is an unrecognized service identifier.", incoming.TypeId)); } // invoke service. return(service.Invoke(incoming)); } catch (Exception e) { // create fault. return(CreateFault(incoming, e)); } }
/// <summary> /// Called before the server starts. /// </summary> /// <param name="configuration">The object that stores the configurable configuration information for a UA application.</param> protected virtual void OnServerStarting(ApplicationConfiguration configuration) { // fetch properties and configuration. Configuration = configuration; ServerProperties = LoadServerProperties(); // ensure at least one security policy exists. if (configuration.ServerConfiguration != null) { if (configuration.ServerConfiguration.SecurityPolicies.Count == 0) { configuration.ServerConfiguration.SecurityPolicies.Add(new ServerSecurityPolicy()); } // ensure at least one user token policy exists. if (configuration.ServerConfiguration.UserTokenPolicies.Count == 0) { UserTokenPolicy userTokenPolicy = new UserTokenPolicy(); userTokenPolicy.TokenType = UserTokenType.Anonymous; userTokenPolicy.PolicyId = userTokenPolicy.TokenType.ToString(); configuration.ServerConfiguration.UserTokenPolicies.Add(userTokenPolicy); } } // load the instance certificate. if (configuration.SecurityConfiguration.ApplicationCertificate != null) { InstanceCertificate = configuration.SecurityConfiguration.ApplicationCertificate.Find(true).Result; } if (InstanceCertificate == null) { throw new ServiceResultException( StatusCodes.BadConfigurationError, "Server does not have an instance certificate assigned."); } if (!InstanceCertificate.HasPrivateKey) { throw new ServiceResultException( StatusCodes.BadConfigurationError, "Server does not have access to the private key for the instance certificate."); } // load certificate chain. InstanceCertificateChain = new X509Certificate2Collection(InstanceCertificate); List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); configuration.CertificateValidator.GetIssuers(InstanceCertificateChain, issuers).Wait(); for (int i = 0; i < issuers.Count; i++) { InstanceCertificateChain.Add(issuers[i].Certificate); } // use the message context from the configuration to ensure the channels are using the same one. MessageContext = configuration.CreateMessageContext(); // assign a unique identifier if none specified. if (String.IsNullOrEmpty(configuration.ApplicationUri)) { configuration.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(InstanceCertificate); if (String.IsNullOrEmpty(configuration.ApplicationUri)) { configuration.ApplicationUri = Utils.Format( "http://{0}/{1}/{2}", Utils.GetHostName(), configuration.ApplicationName, Guid.NewGuid()); } } // initialize namespace table. MessageContext.NamespaceUris = new NamespaceTable(); MessageContext.NamespaceUris.Append(configuration.ApplicationUri); // assign an instance name. if (String.IsNullOrEmpty(configuration.ApplicationName) && InstanceCertificate != null) { configuration.ApplicationName = InstanceCertificate.GetNameInfo(X509NameType.DnsName, false); } // save the certificate validator. CertificateValidator = configuration.CertificateValidator; }
/// <summary> /// Recursively collects the nodes within a type hierarchy. /// </summary> private void UpdateInstanceHierarchyWithInstance( HierarchyBrowsePath parent, ExpandedNodeId instanceId, InstanceDeclarationHierarchy hierarchy) { INode instance = m_nodes.Find(instanceId) as INode; // ignore instances not in the address space. if (instance == null) { return; } // must be an object, variable or method. if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0) { return; } // construct the browse path that identifies the node. string browsePath = null; if (parent.BrowsePath == "/") { browsePath = Utils.Format("/{0}", instance.BrowseName); } else { browsePath = Utils.Format("{0}/{1}", parent.BrowsePath, instance.BrowseName); } // check if the browse path exists in the hierarchy. HierarchyBrowsePath child = null; if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out child)) { return; } // update the instance. child.InstanceId = instance.NodeId; // check if already followed. if (hierarchy.Instances.ContainsKey((NodeId)instance.NodeId)) { return; } // save child. hierarchy.Instances.Add((NodeId)instance.NodeId, instance); // check for local node. ILocalNode localInstance = instance as ILocalNode; if (localInstance == null) { return; } // follow children. foreach (IReference reference in localInstance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree)) { UpdateInstanceHierarchyWithInstance(child, reference.TargetId, hierarchy); } }
/// <summary> /// Converts an ContentFilterElement to a displayable string. /// </summary> /// <param name="nodeTable">The node table.</param> /// <returns>ContentFilterElement as a displayable string.</returns> public virtual string ToString(INodeTable nodeTable) { List <FilterOperand> operands = GetOperands(); string operand1 = (operands.Count > 0)?operands[0].ToString(nodeTable):null; string operand2 = (operands.Count > 1)?operands[1].ToString(nodeTable):null; string operand3 = (operands.Count > 2)?operands[2].ToString(nodeTable):null; StringBuilder buffer = new StringBuilder(); switch (FilterOperator) { case FilterOperator.OfType: case FilterOperator.InView: case FilterOperator.IsNull: case FilterOperator.Not: { buffer.AppendFormat("{0} '{1}'", FilterOperator, operand1); break; } case FilterOperator.And: case FilterOperator.Equals: case FilterOperator.GreaterThan: case FilterOperator.GreaterThanOrEqual: case FilterOperator.LessThan: case FilterOperator.LessThanOrEqual: case FilterOperator.Like: case FilterOperator.Or: { buffer.AppendFormat("'{1}' {0} '{2}'", FilterOperator, operand1, operand2); break; } case FilterOperator.Between: { buffer.AppendFormat("'{1}' <= '{0}' <= '{2}'", operand1, operand2, operand3); break; } case FilterOperator.Cast: { buffer.AppendFormat("({1}){0}", operand1, operand2); break; } case FilterOperator.InList: { buffer.AppendFormat("'{0}' in {", operand1); for (int ii = 1; ii < operands.Count; ii++) { if (ii < operands.Count - 1) { buffer.Append(", "); } buffer.AppendFormat("'{0}'", operands[ii].ToString()); } buffer.Append("}"); break; } case FilterOperator.RelatedTo: { buffer.AppendFormat("'{0}' ", operand1); string referenceType = operand2; if (operands.Count > 1) { LiteralOperand literalOperand = operands[1] as LiteralOperand; if (literalOperand != null) { INode node = nodeTable.Find(literalOperand.Value.Value as NodeId); if (node != null) { referenceType = Utils.Format("{0}", node); } } } buffer.AppendFormat("{0} '{1}'", referenceType, operand2); if (operand3 != null) { buffer.AppendFormat("Hops='{0}'", operand3); } break; } } return(buffer.ToString()); }
/// <summary> /// Converts an FilterOperand to a displayable string. /// </summary> /// <param name="nodeTable">The node table.</param> /// <returns>ContentFilterElement as a displayable string.</returns> public virtual string ToString(INodeTable nodeTable) { return(Utils.Format("{0}", this)); }
/// <summary> /// Converts an ElementOperand to a displayable string. /// </summary> /// <param name="table">The table.</param> /// <returns>ElementOperand as a displayable string.</returns> public override string ToString(INodeTable table) { return(Utils.Format("Element[{0}]", Index)); }
/// <summary> /// Sets the parameters to suitable defaults. /// </summary> private static void SetSuitableDefaults( ref string applicationUri, ref string applicationName, ref string subjectName, ref IList <String> domainNames) { // parse the subject name if specified. List <string> subjectNameEntries = null; if (!String.IsNullOrEmpty(subjectName)) { subjectNameEntries = X509Utils.ParseDistinguishedName(subjectName); } // check the application name. if (String.IsNullOrEmpty(applicationName)) { if (subjectNameEntries == null) { throw new ArgumentNullException(nameof(applicationName), "Must specify a applicationName or a subjectName."); } // use the common name as the application name. for (int ii = 0; ii < subjectNameEntries.Count; ii++) { if (subjectNameEntries[ii].StartsWith("CN=", StringComparison.Ordinal)) { applicationName = subjectNameEntries[ii].Substring(3).Trim(); break; } } } if (String.IsNullOrEmpty(applicationName)) { throw new ArgumentNullException(nameof(applicationName), "Must specify a applicationName or a subjectName."); } // remove special characters from name. StringBuilder buffer = new StringBuilder(); for (int ii = 0; ii < applicationName.Length; ii++) { char ch = applicationName[ii]; if (Char.IsControl(ch) || ch == '/' || ch == ',' || ch == ';') { ch = '+'; } buffer.Append(ch); } applicationName = buffer.ToString(); // ensure at least one host name. if (domainNames == null || domainNames.Count == 0) { domainNames = new List <string>(); domainNames.Add(Utils.GetHostName()); } // create the application uri. if (String.IsNullOrEmpty(applicationUri)) { StringBuilder builder = new StringBuilder(); builder.Append("urn:"); builder.Append(domainNames[0]); builder.Append(':'); builder.Append(applicationName); applicationUri = builder.ToString(); } Uri uri = Utils.ParseUri(applicationUri); if (uri == null) { throw new ArgumentNullException(nameof(applicationUri), "Must specify a valid URL."); } // create the subject name, if (String.IsNullOrEmpty(subjectName)) { subjectName = Utils.Format("CN={0}", applicationName); } if (!subjectName.Contains("CN=")) { subjectName = Utils.Format("CN={0}", subjectName); } if (domainNames != null && domainNames.Count > 0) { if (!subjectName.Contains("DC=") && !subjectName.Contains('=')) { subjectName += Utils.Format(", DC={0}", domainNames[0]); } else { subjectName = Utils.ReplaceDCLocalhost(subjectName, domainNames[0]); } } }
/// <summary> /// Returns the string representation of the embededobject. /// </summary> /// <param name="format">(Unused). Leave this as null</param> /// <param name="formatProvider">The provider of a mechanism for retrieving an object to control formatting.</param> /// <returns> /// A <see cref="T:System.String"/> containing the value of the current embeded instance in the specified format. /// </returns> /// <exception cref="FormatException">Thrown if the <i>format</i> parameter is not null</exception> public string ToString(string format, IFormatProvider formatProvider) { if (format == null) { if (m_body is byte[]) { return(String.Format(formatProvider, "Byte[{0}]", ((byte[])m_body).Length)); } if (m_body is XmlElement) { return(String.Format(formatProvider, "<{0}>", ((XmlElement)m_body).Name)); } if (m_body is IEncodeable) { StringBuilder body = new StringBuilder(); PropertyInfo[] properties = m_body.GetType().GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance); foreach (PropertyInfo property in properties) { object[] attributes = property.GetCustomAttributes(typeof(DataMemberAttribute), true); for (int ii = 0; ii < attributes.Length; ii++) { DataMemberAttribute contract = attributes[ii] as DataMemberAttribute; if (contract != null) { if (body.Length == 0) { body.Append("{"); } else { body.Append(" | "); } body.AppendFormat("{0}", property.GetGetMethod().Invoke(m_body, null)); } } } if (body.Length > 0) { body.Append("}"); } return(String.Format(formatProvider, "{0}", body)); } if (!NodeId.IsNull(this.m_typeId)) { return(String.Format(formatProvider, "{{{0}}}", this.m_typeId)); } return("(null)"); } throw new FormatException(Utils.Format("Invalid format string: '{0}'.", format)); }
/// <summary> /// Writes the given value in raw format without meta information. /// </summary> /// <param name="encoder">The encoder to use.</param> /// <param name="fieldName">The name of the field.</param> /// <param name="value">The value to write.</param> /// <param name="typeInfo">The type of the value.</param> /// <exception cref="NotImplementedException"></exception> /// <exception cref="ServiceResultException"></exception> public static void WriteRaw(this IEncoder encoder, string fieldName, object value, TypeInfo typeInfo) { if (value != null) { if (typeInfo.ValueRank < 0) { switch (typeInfo.BuiltInType) { case BuiltInType.Boolean: encoder.WriteBoolean(fieldName, (bool)value); return; case BuiltInType.SByte: encoder.WriteSByte(fieldName, (sbyte)value); return; case BuiltInType.Byte: encoder.WriteByte(fieldName, (byte)value); return; case BuiltInType.Int16: encoder.WriteInt16(fieldName, (short)value); return; case BuiltInType.UInt16: encoder.WriteUInt16(fieldName, (ushort)value); return; case BuiltInType.Int32: encoder.WriteInt32(fieldName, (int)value); return; case BuiltInType.UInt32: encoder.WriteUInt32(fieldName, (uint)value); return; case BuiltInType.Int64: encoder.WriteInt64(fieldName, (long)value); return; case BuiltInType.UInt64: encoder.WriteUInt64(fieldName, (ulong)value); return; case BuiltInType.Float: encoder.WriteFloat(fieldName, (float)value); return; case BuiltInType.Double: encoder.WriteDouble(fieldName, (double)value); return; case BuiltInType.String: encoder.WriteString(fieldName, (string)value); return; case BuiltInType.DateTime: encoder.WriteDateTime(fieldName, (DateTime)value); return; case BuiltInType.Guid: encoder.WriteGuid(fieldName, (Uuid)value); return; case BuiltInType.ByteString: encoder.WriteByteString(fieldName, (byte[])value); return; case BuiltInType.XmlElement: encoder.WriteXmlElement(fieldName, (XmlElement)value); return; case BuiltInType.NodeId: encoder.WriteNodeId(fieldName, (NodeId)value); return; case BuiltInType.ExpandedNodeId: encoder.WriteExpandedNodeId(fieldName, (ExpandedNodeId)value); return; case BuiltInType.StatusCode: encoder.WriteStatusCode(fieldName, (StatusCode)value); return; case BuiltInType.QualifiedName: encoder.WriteQualifiedName(fieldName, (QualifiedName)value); return; case BuiltInType.LocalizedText: encoder.WriteLocalizedText(fieldName, (LocalizedText)value); return; case BuiltInType.ExtensionObject: encoder.WriteExtensionObject(fieldName, (ExtensionObject)value); return; case BuiltInType.DataValue: encoder.WriteDataValue(fieldName, (DataValue)value); return; case BuiltInType.Enumeration: encoder.WriteInt32(fieldName, (int)value); return; } } else if (typeInfo.ValueRank <= 1) { switch (typeInfo.BuiltInType) { case BuiltInType.Boolean: encoder.WriteBooleanArray(fieldName, (bool[])value); return; case BuiltInType.SByte: encoder.WriteSByteArray(fieldName, (sbyte[])value); return; case BuiltInType.Byte: encoder.WriteByteArray(fieldName, (byte[])value); return; case BuiltInType.Int16: encoder.WriteInt16Array(fieldName, (short[])value); return; case BuiltInType.UInt16: encoder.WriteUInt16Array(fieldName, (ushort[])value); return; case BuiltInType.Int32: encoder.WriteInt32Array(fieldName, (int[])value); return; case BuiltInType.UInt32: encoder.WriteUInt32Array(fieldName, (uint[])value); return; case BuiltInType.Int64: encoder.WriteInt64Array(fieldName, (long[])value); return; case BuiltInType.UInt64: encoder.WriteUInt64Array(fieldName, (ulong[])value); return; case BuiltInType.Float: encoder.WriteFloatArray(fieldName, (float[])value); return; case BuiltInType.Double: encoder.WriteDoubleArray(fieldName, (double[])value); return; case BuiltInType.String: encoder.WriteStringArray(fieldName, (string[])value); return; case BuiltInType.DateTime: encoder.WriteDateTimeArray(fieldName, (DateTime[])value); return; case BuiltInType.Guid: encoder.WriteGuidArray(fieldName, (Uuid[])value); return; case BuiltInType.ByteString: encoder.WriteByteStringArray(fieldName, (byte[][])value); return; case BuiltInType.XmlElement: encoder.WriteXmlElementArray(fieldName, (XmlElement[])value); return; case BuiltInType.NodeId: encoder.WriteNodeIdArray(fieldName, (NodeId[])value); return; case BuiltInType.ExpandedNodeId: encoder.WriteExpandedNodeIdArray(fieldName, (ExpandedNodeId[])value); return; case BuiltInType.StatusCode: encoder.WriteStatusCodeArray(fieldName, (StatusCode[])value); return; case BuiltInType.QualifiedName: encoder.WriteQualifiedNameArray(fieldName, (QualifiedName[])value); return; case BuiltInType.LocalizedText: encoder.WriteLocalizedTextArray(fieldName, (LocalizedText[])value); return; case BuiltInType.ExtensionObject: encoder.WriteExtensionObjectArray(fieldName, (ExtensionObject[])value); return; case BuiltInType.DataValue: encoder.WriteDataValueArray(fieldName, (DataValue[])value); return; case BuiltInType.Variant: if (value is Variant[] variantArray) { encoder.WriteVariantArray(fieldName, variantArray); return; } if (value is object[] objArray) { throw new NotImplementedException(); } throw ServiceResultException.Create(2147876864U, "Unexpected type encountered while encoding an array of Variants: {0}", (object)value.GetType()); case BuiltInType.Enumeration: var enumArray = value as Enum[]; var strArray = new string[enumArray.Length]; for (var index = 0; index < enumArray.Length; ++index) { var str = enumArray[index].ToString() + "_" + enumArray[index].As <int>().ToString(CultureInfo.InvariantCulture); strArray[index] = str; } encoder.WriteStringArray(null, strArray); return; } } else if (typeInfo.ValueRank > 1) { encoder.WriteMatrix(fieldName, (Matrix)value); return; } throw new ServiceResultException(2147876864U, Utils.Format("Type '{0}' is not allowed in an Variant.", (object)value.GetType().FullName)); } }
/// <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); } }