mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 07:07:24 +08:00
Merge pull request #385 from AlinMoldovean/v1.03
Implement certificate chain support for #362
This commit is contained in:
@@ -199,7 +199,7 @@ namespace Opc.Ua.Client
|
||||
m_instanceCertificate.Thumbprint);
|
||||
}
|
||||
|
||||
// load certificate chain
|
||||
// load certificate chain.
|
||||
m_instanceCertificateChain = new X509Certificate2Collection(m_instanceCertificate);
|
||||
List<CertificateIdentifier> issuers = new List<CertificateIdentifier>();
|
||||
configuration.CertificateValidator.GetIssuers(m_instanceCertificate, issuers).Wait();
|
||||
@@ -835,6 +835,7 @@ namespace Opc.Ua.Client
|
||||
}
|
||||
|
||||
X509Certificate2 clientCertificate = null;
|
||||
X509Certificate2Collection clientCertificateChain = null;
|
||||
|
||||
if (endpointDescription.SecurityPolicyUri != SecurityPolicies.None)
|
||||
{
|
||||
@@ -849,6 +850,19 @@ namespace Opc.Ua.Client
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "ApplicationCertificate cannot be found.");
|
||||
}
|
||||
|
||||
// load certificate chain.
|
||||
if (configuration.SecurityConfiguration.SendCertificateChain)
|
||||
{
|
||||
clientCertificateChain = new X509Certificate2Collection(clientCertificate);
|
||||
List<CertificateIdentifier> issuers = new List<CertificateIdentifier>();
|
||||
await configuration.CertificateValidator.GetIssuers(clientCertificate, issuers);
|
||||
|
||||
for (int i = 0; i < issuers.Count; i++)
|
||||
{
|
||||
clientCertificateChain.Add(issuers[i].Certificate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize the channel which will be created with the server.
|
||||
@@ -857,6 +871,7 @@ namespace Opc.Ua.Client
|
||||
endpointDescription,
|
||||
endpointConfiguration,
|
||||
clientCertificate,
|
||||
clientCertificateChain,
|
||||
messageContext);
|
||||
|
||||
// create the session object.
|
||||
@@ -890,6 +905,7 @@ namespace Opc.Ua.Client
|
||||
template.m_endpoint.Description,
|
||||
template.m_endpoint.Configuration,
|
||||
template.m_instanceCertificate,
|
||||
template.m_configuration.SecurityConfiguration.SendCertificateChain ? template.m_instanceCertificateChain : null,
|
||||
template.m_configuration.CreateMessageContext());
|
||||
|
||||
// create the session object.
|
||||
@@ -968,10 +984,9 @@ namespace Opc.Ua.Client
|
||||
}
|
||||
}
|
||||
|
||||
EndpointDescription endpoint = m_endpoint.Description;
|
||||
|
||||
// create the client signature.
|
||||
byte[] dataToSign = Utils.Append(endpoint.ServerCertificate, m_serverNonce);
|
||||
byte[] dataToSign = Utils.Append(m_serverCertificate != null ? m_serverCertificate.RawData : null, m_serverNonce);
|
||||
EndpointDescription endpoint = m_endpoint.Description;
|
||||
SignatureData clientSignature = SecurityPolicies.Sign(m_instanceCertificate, endpoint.SecurityPolicyUri, dataToSign);
|
||||
|
||||
// check that the user identity is supported by the endpoint.
|
||||
@@ -1026,6 +1041,7 @@ namespace Opc.Ua.Client
|
||||
m_endpoint.Description,
|
||||
m_endpoint.Configuration,
|
||||
m_instanceCertificate,
|
||||
m_configuration.SecurityConfiguration.SendCertificateChain ? m_instanceCertificateChain : null,
|
||||
MessageContext);
|
||||
|
||||
// disposes the existing channel.
|
||||
@@ -1978,7 +1994,7 @@ namespace Opc.Ua.Client
|
||||
|
||||
string securityPolicyUri = m_endpoint.Description.SecurityPolicyUri;
|
||||
|
||||
// get the identity token.
|
||||
// get the identity token.
|
||||
if (identity == null)
|
||||
{
|
||||
identity = new UserIdentity();
|
||||
@@ -2011,13 +2027,20 @@ namespace Opc.Ua.Client
|
||||
requireEncryption = identityPolicy.SecurityPolicyUri != SecurityPolicies.None;
|
||||
}
|
||||
|
||||
// validate the server certificate.
|
||||
// validate the server certificate /certificate chain.
|
||||
X509Certificate2 serverCertificate = null;
|
||||
byte[] certificateData = m_endpoint.Description.ServerCertificate;
|
||||
|
||||
if (certificateData != null && certificateData.Length > 0 && requireEncryption)
|
||||
{
|
||||
serverCertificate = Utils.ParseCertificateBlob(certificateData);
|
||||
m_configuration.CertificateValidator.Validate(serverCertificate);
|
||||
X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(certificateData);
|
||||
|
||||
if (serverCertificateChain.Count > 0)
|
||||
{
|
||||
serverCertificate = serverCertificateChain[0];
|
||||
}
|
||||
|
||||
m_configuration.CertificateValidator.Validate(serverCertificateChain);
|
||||
|
||||
if (checkDomain)
|
||||
{
|
||||
@@ -2025,11 +2048,6 @@ namespace Opc.Ua.Client
|
||||
}
|
||||
// save for reconnect
|
||||
m_checkDomain = checkDomain;
|
||||
|
||||
//X509Certificate2Collection certificateChain = Utils.ParseCertificateChainBlob(certificateData);
|
||||
//if (certificateChain.Count > 0)
|
||||
// serverCertificate = certificateChain[0];
|
||||
//m_configuration.CertificateValidator.Validate(certificateChain);
|
||||
}
|
||||
|
||||
// create a nonce.
|
||||
@@ -2045,6 +2063,19 @@ namespace Opc.Ua.Client
|
||||
|
||||
// send the application instance certificate for the client.
|
||||
byte[] clientCertificateData = m_instanceCertificate != null ? m_instanceCertificate.RawData : null;
|
||||
byte[] clientCertificateChainData = null;
|
||||
|
||||
if (m_instanceCertificateChain != null && m_instanceCertificateChain.Count > 0 && m_configuration.SecurityConfiguration.SendCertificateChain)
|
||||
{
|
||||
List<byte> clientCertificateChain = new List<byte>();
|
||||
|
||||
for (int i = 0; i < m_instanceCertificateChain.Count; i++)
|
||||
{
|
||||
clientCertificateChain.AddRange(m_instanceCertificateChain[i].RawData);
|
||||
}
|
||||
|
||||
clientCertificateChainData = clientCertificateChain.ToArray();
|
||||
}
|
||||
|
||||
ApplicationDescription clientDescription = new ApplicationDescription();
|
||||
|
||||
@@ -2103,7 +2134,7 @@ namespace Opc.Ua.Client
|
||||
m_endpoint.EndpointUrl.ToString(),
|
||||
sessionName,
|
||||
clientNonce,
|
||||
clientCertificateData,
|
||||
clientCertificateChainData != null ? clientCertificateChainData : clientCertificateData,
|
||||
sessionTimeout,
|
||||
(uint)MessageContext.MaxMessageSize,
|
||||
out sessionId,
|
||||
@@ -2129,13 +2160,27 @@ namespace Opc.Ua.Client
|
||||
//we need to call CloseSession if CreateSession was successful but some other exception is thrown
|
||||
try
|
||||
{
|
||||
|
||||
// verify that the server returned the same instance certificate.
|
||||
if (serverCertificateData != null && !Utils.IsEqual(serverCertificateData, m_endpoint.Description.ServerCertificate))
|
||||
{
|
||||
throw ServiceResultException.Create(
|
||||
StatusCodes.BadCertificateInvalid,
|
||||
"Server did not return the certificate used to create the secure channel.");
|
||||
try
|
||||
{
|
||||
// verify for certificate chain in endpoint.
|
||||
X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(m_endpoint.Description.ServerCertificate);
|
||||
|
||||
if (serverCertificateChain.Count > 0 && !Utils.IsEqual(serverCertificateData, serverCertificateChain[0].RawData))
|
||||
{
|
||||
throw ServiceResultException.Create(
|
||||
StatusCodes.BadCertificateInvalid,
|
||||
"Server did not return the certificate used to create the secure channel.");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw ServiceResultException.Create(
|
||||
StatusCodes.BadCertificateInvalid,
|
||||
"Server did not return the certificate used to create the secure channel.");
|
||||
}
|
||||
}
|
||||
|
||||
if (serverSignature == null || serverSignature.Signature == null)
|
||||
@@ -2191,7 +2236,6 @@ namespace Opc.Ua.Client
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// find the matching description (TBD - check domains against certificate).
|
||||
bool found = false;
|
||||
Uri expectedUrl = Utils.ParseUri(m_endpoint.Description.EndpointUrl);
|
||||
@@ -2238,9 +2282,24 @@ namespace Opc.Ua.Client
|
||||
|
||||
if (!SecurityPolicies.Verify(serverCertificate, m_endpoint.Description.SecurityPolicyUri, dataToSign, serverSignature))
|
||||
{
|
||||
throw ServiceResultException.Create(
|
||||
StatusCodes.BadApplicationSignatureInvalid,
|
||||
"Server did not provide a correct signature for the nonce data provided by the client.");
|
||||
// validate the signature with complete chain if the check with leaf certificate failed.
|
||||
if (clientCertificateChainData != null)
|
||||
{
|
||||
dataToSign = Utils.Append(clientCertificateChainData, clientNonce);
|
||||
|
||||
if (!SecurityPolicies.Verify(serverCertificate, m_endpoint.Description.SecurityPolicyUri, dataToSign, serverSignature))
|
||||
{
|
||||
throw ServiceResultException.Create(
|
||||
StatusCodes.BadApplicationSignatureInvalid,
|
||||
"Server did not provide a correct signature for the nonce data provided by the client.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ServiceResultException.Create(
|
||||
StatusCodes.BadApplicationSignatureInvalid,
|
||||
"Server did not provide a correct signature for the nonce data provided by the client.");
|
||||
}
|
||||
}
|
||||
|
||||
// get a validator to check certificates provided by server.
|
||||
@@ -2270,7 +2329,7 @@ namespace Opc.Ua.Client
|
||||
ValidateSoftwareCertificates(softwareCertificates);
|
||||
|
||||
// create the client signature.
|
||||
dataToSign = Utils.Append(serverCertificateData, serverNonce);
|
||||
dataToSign = Utils.Append(serverCertificate != null ? serverCertificate.RawData : null, serverNonce);
|
||||
SignatureData clientSignature = SecurityPolicies.Sign(m_instanceCertificate, securityPolicyUri, dataToSign);
|
||||
|
||||
// select the security policy for the user token.
|
||||
@@ -2406,13 +2465,7 @@ namespace Opc.Ua.Client
|
||||
string securityPolicyUri = m_endpoint.Description.SecurityPolicyUri;
|
||||
|
||||
// create the client signature.
|
||||
byte[] serverCertificateData = null;
|
||||
if (m_serverCertificate != null)
|
||||
{
|
||||
serverCertificateData = m_serverCertificate.RawData;
|
||||
}
|
||||
// create the client signature.
|
||||
byte[] dataToSign = Utils.Append(serverCertificateData, serverNonce);
|
||||
byte[] dataToSign = Utils.Append(m_serverCertificate != null ? m_serverCertificate.RawData : null, serverNonce);
|
||||
SignatureData clientSignature = SecurityPolicies.Sign(m_instanceCertificate, securityPolicyUri, dataToSign);
|
||||
|
||||
// choose a default token.
|
||||
|
||||
@@ -341,7 +341,8 @@ namespace Opc.Ua.Server
|
||||
{
|
||||
try
|
||||
{
|
||||
parsedClientCertificate = CertificateFactory.Create(clientCertificate, true);
|
||||
X509Certificate2Collection clientCertificateChain = Utils.ParseCertificateChainBlob(clientCertificate);
|
||||
parsedClientCertificate = clientCertificateChain[0];
|
||||
|
||||
if (context.SecurityPolicyUri != SecurityPolicies.None)
|
||||
{
|
||||
@@ -357,7 +358,7 @@ namespace Opc.Ua.Server
|
||||
"The URI specified in the ApplicationDescription does not match the URI in the Certificate.");
|
||||
}
|
||||
|
||||
CertificateValidator.Validate(parsedClientCertificate);
|
||||
CertificateValidator.Validate(clientCertificateChain);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -395,29 +396,44 @@ namespace Opc.Ua.Server
|
||||
out sessionId,
|
||||
out authenticationToken,
|
||||
out serverNonce,
|
||||
out revisedSessionTimeout);
|
||||
|
||||
out revisedSessionTimeout);
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
{
|
||||
// return the application instance certificate for the server.
|
||||
if (requireEncryption)
|
||||
{
|
||||
serverCertificate = InstanceCertificate.RawData;
|
||||
// check if complete chain should be sent.
|
||||
if (Configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count >0)
|
||||
{
|
||||
List<byte> serverCertificateChain = new List<byte>();
|
||||
|
||||
for (int i = 0; i < InstanceCertificateChain.Count; i++)
|
||||
{
|
||||
serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData);
|
||||
}
|
||||
|
||||
serverCertificate = serverCertificateChain.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
serverCertificate = InstanceCertificate.RawData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// return the endpoints supported by the server.
|
||||
serverEndpoints = GetEndpointDescriptions(endpointUrl, BaseAddresses, null);
|
||||
|
||||
// return the software certificates assigned to the server.
|
||||
serverSoftwareCertificates = new SignedSoftwareCertificateCollection(ServerProperties.SoftwareCertificates);
|
||||
|
||||
|
||||
// sign the nonce provided by the client.
|
||||
serverSignature = null;
|
||||
|
||||
|
||||
// sign the client nonce (if provided).
|
||||
if (parsedClientCertificate != null && clientNonce != null)
|
||||
{
|
||||
byte[] dataToSign = Utils.Append(clientCertificate, clientNonce);
|
||||
byte[] dataToSign = Utils.Append(parsedClientCertificate.RawData, clientNonce);
|
||||
serverSignature = SecurityPolicies.Sign(InstanceCertificate, context.SecurityPolicyUri, dataToSign);
|
||||
}
|
||||
}
|
||||
@@ -2682,7 +2698,7 @@ namespace Opc.Ua.Server
|
||||
serverDescription.ApplicationType = configuration.ApplicationType;
|
||||
serverDescription.ProductUri = configuration.ProductUri;
|
||||
serverDescription.DiscoveryUrls = GetDiscoveryUrls();
|
||||
|
||||
|
||||
endpoints = new EndpointDescriptionCollection();
|
||||
IList<EndpointDescription> endpointsForHost = null;
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ namespace Opc.Ua.Server
|
||||
byte[] clientNonce,
|
||||
byte[] serverNonce,
|
||||
string sessionName,
|
||||
ApplicationDescription clientDescription,
|
||||
string endpointUrl,
|
||||
X509Certificate2 clientCertificate,
|
||||
ApplicationDescription clientDescription,
|
||||
string endpointUrl,
|
||||
X509Certificate2 clientCertificate,
|
||||
double sessionTimeout,
|
||||
uint maxResponseMessageSize,
|
||||
double maxRequestAge,
|
||||
@@ -547,10 +547,34 @@ namespace Opc.Ua.Server
|
||||
|
||||
if (!SecurityPolicies.Verify(m_clientCertificate, m_endpoint.SecurityPolicyUri, dataToSign, clientSignature))
|
||||
{
|
||||
throw new ServiceResultException(StatusCodes.BadApplicationSignatureInvalid);
|
||||
// verify for certificate chain in endpoint.
|
||||
// validate the signature with complete chain if the check with leaf certificate failed.
|
||||
X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(m_endpoint.ServerCertificate);
|
||||
|
||||
if (serverCertificateChain.Count > 1)
|
||||
{
|
||||
List<byte> serverCertificateChainList = new List<byte>();
|
||||
|
||||
for (int i = 0; i < serverCertificateChain.Count; i++)
|
||||
{
|
||||
serverCertificateChainList.AddRange(serverCertificateChain[i].RawData);
|
||||
}
|
||||
|
||||
byte[] serverCertificateChainData = serverCertificateChainList.ToArray();
|
||||
dataToSign = Utils.Append(serverCertificateChainData, m_serverNonce);
|
||||
|
||||
if (!SecurityPolicies.Verify(m_clientCertificate, m_endpoint.SecurityPolicyUri, dataToSign, clientSignature))
|
||||
{
|
||||
throw new ServiceResultException(StatusCodes.BadApplicationSignatureInvalid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServiceResultException(StatusCodes.BadApplicationSignatureInvalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!m_activated)
|
||||
{
|
||||
// must active the session on the channel that was used to create it.
|
||||
@@ -565,7 +589,7 @@ namespace Opc.Ua.Server
|
||||
if (clientSoftwareCertificates != null && clientSoftwareCertificates.Count > 0)
|
||||
{
|
||||
throw new ServiceResultException(StatusCodes.BadInvalidArgument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate the user identity token.
|
||||
@@ -1024,11 +1048,35 @@ namespace Opc.Ua.Server
|
||||
|
||||
if (!token.Verify(dataToSign, userTokenSignature, securityPolicyUri))
|
||||
{
|
||||
throw new ServiceResultException(StatusCodes.BadUserSignatureInvalid, "Invalid user signature!");
|
||||
// verify for certificate chain in endpoint.
|
||||
// validate the signature with complete chain if the check with leaf certificate failed.
|
||||
X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(m_endpoint.ServerCertificate);
|
||||
|
||||
if (serverCertificateChain.Count > 1)
|
||||
{
|
||||
List<byte> serverCertificateChainList = new List<byte>();
|
||||
|
||||
for (int i = 0; i < serverCertificateChain.Count; i++)
|
||||
{
|
||||
serverCertificateChainList.AddRange(serverCertificateChain[i].RawData);
|
||||
}
|
||||
|
||||
byte[] serverCertificateChainData = serverCertificateChainList.ToArray();
|
||||
dataToSign = Utils.Append(serverCertificateChainData, m_serverNonce);
|
||||
|
||||
if (!token.Verify(dataToSign, userTokenSignature, securityPolicyUri))
|
||||
{
|
||||
throw new ServiceResultException(StatusCodes.BadUserSignatureInvalid, "Invalid user signature!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServiceResultException(StatusCodes.BadUserSignatureInvalid, "Invalid user signature!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// validate user identity token.
|
||||
return token;
|
||||
}
|
||||
@@ -1043,7 +1091,7 @@ namespace Opc.Ua.Server
|
||||
IUserIdentity effectiveIdentity)
|
||||
{
|
||||
if (identityToken == null) throw new ArgumentNullException("identityToken");
|
||||
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
bool changed = m_effectiveIdentity == null && effectiveIdentity != null;
|
||||
@@ -1057,7 +1105,7 @@ namespace Opc.Ua.Server
|
||||
m_identityToken = identityToken;
|
||||
m_identity = identity;
|
||||
m_effectiveIdentity = effectiveIdentity;
|
||||
|
||||
|
||||
// update diagnostics.
|
||||
lock (DiagnosticsLock)
|
||||
{
|
||||
@@ -1097,39 +1145,39 @@ namespace Opc.Ua.Server
|
||||
}
|
||||
|
||||
ServiceCounterDataType counter = null;
|
||||
|
||||
|
||||
switch (requestType)
|
||||
{
|
||||
case RequestType.Read: { counter = m_diagnostics.ReadCount; break; }
|
||||
case RequestType.HistoryRead: { counter = m_diagnostics.HistoryReadCount; break; }
|
||||
case RequestType.Write: { counter = m_diagnostics.WriteCount; break; }
|
||||
case RequestType.HistoryUpdate: { counter = m_diagnostics.HistoryUpdateCount; break; }
|
||||
case RequestType.Call: { counter = m_diagnostics.CallCount; break; }
|
||||
case RequestType.CreateMonitoredItems: { counter = m_diagnostics.CreateMonitoredItemsCount; break; }
|
||||
case RequestType.ModifyMonitoredItems: { counter = m_diagnostics.ModifyMonitoredItemsCount; break; }
|
||||
case RequestType.SetMonitoringMode: { counter = m_diagnostics.SetMonitoringModeCount; break; }
|
||||
case RequestType.SetTriggering: { counter = m_diagnostics.SetTriggeringCount; break; }
|
||||
case RequestType.DeleteMonitoredItems: { counter = m_diagnostics.DeleteMonitoredItemsCount; break; }
|
||||
case RequestType.CreateSubscription: { counter = m_diagnostics.CreateSubscriptionCount; break; }
|
||||
case RequestType.ModifySubscription: { counter = m_diagnostics.ModifySubscriptionCount; break; }
|
||||
case RequestType.SetPublishingMode: { counter = m_diagnostics.SetPublishingModeCount; break; }
|
||||
case RequestType.Publish: { counter = m_diagnostics.PublishCount; break; }
|
||||
case RequestType.Republish: { counter = m_diagnostics.RepublishCount; break; }
|
||||
case RequestType.TransferSubscriptions: { counter = m_diagnostics.TransferSubscriptionsCount; break; }
|
||||
case RequestType.DeleteSubscriptions: { counter = m_diagnostics.DeleteSubscriptionsCount; break; }
|
||||
case RequestType.AddNodes: { counter = m_diagnostics.AddNodesCount; break; }
|
||||
case RequestType.AddReferences: { counter = m_diagnostics.AddReferencesCount; break; }
|
||||
case RequestType.DeleteNodes: { counter = m_diagnostics.DeleteNodesCount; break; }
|
||||
case RequestType.DeleteReferences: { counter = m_diagnostics.DeleteReferencesCount; break; }
|
||||
case RequestType.Browse: { counter = m_diagnostics.BrowseCount; break; }
|
||||
case RequestType.BrowseNext: { counter = m_diagnostics.BrowseNextCount; break; }
|
||||
case RequestType.TranslateBrowsePathsToNodeIds: { counter = m_diagnostics.TranslateBrowsePathsToNodeIdsCount; break; }
|
||||
case RequestType.QueryFirst: { counter = m_diagnostics.QueryFirstCount; break; }
|
||||
case RequestType.QueryNext: { counter = m_diagnostics.QueryNextCount; break; }
|
||||
case RequestType.RegisterNodes: { counter = m_diagnostics.RegisterNodesCount; break; }
|
||||
case RequestType.UnregisterNodes: { counter = m_diagnostics.UnregisterNodesCount; break; }
|
||||
{
|
||||
case RequestType.Read: { counter = m_diagnostics.ReadCount; break; }
|
||||
case RequestType.HistoryRead: { counter = m_diagnostics.HistoryReadCount; break; }
|
||||
case RequestType.Write: { counter = m_diagnostics.WriteCount; break; }
|
||||
case RequestType.HistoryUpdate: { counter = m_diagnostics.HistoryUpdateCount; break; }
|
||||
case RequestType.Call: { counter = m_diagnostics.CallCount; break; }
|
||||
case RequestType.CreateMonitoredItems: { counter = m_diagnostics.CreateMonitoredItemsCount; break; }
|
||||
case RequestType.ModifyMonitoredItems: { counter = m_diagnostics.ModifyMonitoredItemsCount; break; }
|
||||
case RequestType.SetMonitoringMode: { counter = m_diagnostics.SetMonitoringModeCount; break; }
|
||||
case RequestType.SetTriggering: { counter = m_diagnostics.SetTriggeringCount; break; }
|
||||
case RequestType.DeleteMonitoredItems: { counter = m_diagnostics.DeleteMonitoredItemsCount; break; }
|
||||
case RequestType.CreateSubscription: { counter = m_diagnostics.CreateSubscriptionCount; break; }
|
||||
case RequestType.ModifySubscription: { counter = m_diagnostics.ModifySubscriptionCount; break; }
|
||||
case RequestType.SetPublishingMode: { counter = m_diagnostics.SetPublishingModeCount; break; }
|
||||
case RequestType.Publish: { counter = m_diagnostics.PublishCount; break; }
|
||||
case RequestType.Republish: { counter = m_diagnostics.RepublishCount; break; }
|
||||
case RequestType.TransferSubscriptions: { counter = m_diagnostics.TransferSubscriptionsCount; break; }
|
||||
case RequestType.DeleteSubscriptions: { counter = m_diagnostics.DeleteSubscriptionsCount; break; }
|
||||
case RequestType.AddNodes: { counter = m_diagnostics.AddNodesCount; break; }
|
||||
case RequestType.AddReferences: { counter = m_diagnostics.AddReferencesCount; break; }
|
||||
case RequestType.DeleteNodes: { counter = m_diagnostics.DeleteNodesCount; break; }
|
||||
case RequestType.DeleteReferences: { counter = m_diagnostics.DeleteReferencesCount; break; }
|
||||
case RequestType.Browse: { counter = m_diagnostics.BrowseCount; break; }
|
||||
case RequestType.BrowseNext: { counter = m_diagnostics.BrowseNextCount; break; }
|
||||
case RequestType.TranslateBrowsePathsToNodeIds: { counter = m_diagnostics.TranslateBrowsePathsToNodeIdsCount; break; }
|
||||
case RequestType.QueryFirst: { counter = m_diagnostics.QueryFirstCount; break; }
|
||||
case RequestType.QueryNext: { counter = m_diagnostics.QueryNextCount; break; }
|
||||
case RequestType.RegisterNodes: { counter = m_diagnostics.RegisterNodesCount; break; }
|
||||
case RequestType.UnregisterNodes: { counter = m_diagnostics.UnregisterNodesCount; break; }
|
||||
}
|
||||
|
||||
|
||||
if (counter != null)
|
||||
{
|
||||
counter.TotalCount = counter.TotalCount + 1;
|
||||
|
||||
@@ -83,6 +83,9 @@
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates</StorePath>
|
||||
</RejectedCertificateStore>
|
||||
|
||||
<!-- Specifies whether the complete certificate chain should be sent for CA signed certificates. -->
|
||||
<SendCertificateChain>false</SendCertificateChain>
|
||||
|
||||
</SecurityConfiguration>
|
||||
|
||||
|
||||
@@ -227,6 +227,7 @@ namespace Opc.Ua.Sample.Controls
|
||||
endpoint.Description,
|
||||
endpoint.Configuration,
|
||||
clientCertificate,
|
||||
m_configuration.SecurityConfiguration.SendCertificateChain ? clientCertificateChain : null,
|
||||
m_messageContext);
|
||||
|
||||
// create the session.
|
||||
|
||||
@@ -796,6 +796,7 @@ namespace Opc.Ua
|
||||
m_rejectSHA1SignedCertificates = true;
|
||||
m_minCertificateKeySize = CertificateFactory.defaultKeySize;
|
||||
m_addAppCertToTrustedStore = true;
|
||||
m_sendCertificateChain = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -943,6 +944,7 @@ namespace Opc.Ua
|
||||
get { return m_minCertificateKeySize; }
|
||||
set { m_minCertificateKeySize = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the application cert should be copied to the trusted store.
|
||||
/// </summary>
|
||||
@@ -955,6 +957,19 @@ namespace Opc.Ua
|
||||
get { return m_addAppCertToTrustedStore; }
|
||||
set { m_addAppCertToTrustedStore = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the application should send the complete certificate chain.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If set to true the complete certificate chain will be sent for CA signed certificates.
|
||||
/// </remarks>
|
||||
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 9)]
|
||||
public bool SendCertificateChain
|
||||
{
|
||||
get { return m_sendCertificateChain; }
|
||||
set { m_sendCertificateChain = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
@@ -968,6 +983,7 @@ namespace Opc.Ua
|
||||
private bool m_rejectSHA1SignedCertificates;
|
||||
private ushort m_minCertificateKeySize;
|
||||
private bool m_addAppCertToTrustedStore;
|
||||
private bool m_sendCertificateChain;
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
<xs:element name="RejectedCertificateStore" type="CertificateStoreIdentifier" minOccurs="0" />
|
||||
<xs:element name="AutoAcceptUntrustedCertificates" type="xs:boolean" minOccurs="0" />
|
||||
<xs:element name="UserRoleDirectory" type="xs:string" minOccurs="0" nillable="true" />
|
||||
<xs:element name="RejectSHA1SignedCertificates" type="xs:boolean" minOccurs="0" />
|
||||
<xs:element name="MinimumCertificateKeySize" type="xs:unsignedShort" minOccurs="0" />
|
||||
<xs:element name="AddAppCertToTrustedStore" type="xs:boolean" minOccurs="0" />
|
||||
<xs:element name="SendCertificateChain" type="xs:boolean" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
@@ -10,19 +10,13 @@
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Security;
|
||||
using Opc.Ua.Bindings;
|
||||
|
||||
namespace Opc.Ua
|
||||
{
|
||||
/// <summary>
|
||||
/// A channel object used by clients to access a UA service.
|
||||
/// </summary>
|
||||
/// A channel object used by clients to access a UA service.
|
||||
/// </summary>
|
||||
public partial class SessionChannel
|
||||
{
|
||||
#region Constructors
|
||||
@@ -41,6 +35,27 @@ namespace Opc.Ua
|
||||
EndpointConfiguration endpointConfiguration,
|
||||
X509Certificate2 clientCertificate,
|
||||
ServiceMessageContext messageContext)
|
||||
{
|
||||
return Create(configuration, description, endpointConfiguration, clientCertificate, null, messageContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new transport channel that supports the ISessionChannel service contract.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The application configuration.</param>
|
||||
/// <param name="description">The description for the endpoint.</param>
|
||||
/// <param name="endpointConfiguration">The configuration to use with the endpoint.</param>
|
||||
/// <param name="clientCertificate">The client certificate.</param>
|
||||
/// <param name="clientCertificateChain">The client certificate chain.</param>
|
||||
/// <param name="messageContext">The message context to use when serializing the messages.</param>
|
||||
/// <returns></returns>
|
||||
public static ITransportChannel Create(
|
||||
ApplicationConfiguration configuration,
|
||||
EndpointDescription description,
|
||||
EndpointConfiguration endpointConfiguration,
|
||||
X509Certificate2 clientCertificate,
|
||||
X509Certificate2Collection clientCertificateChain,
|
||||
ServiceMessageContext messageContext)
|
||||
{
|
||||
// create a UA binary channel.
|
||||
ITransportChannel channel = CreateUaBinaryChannel(
|
||||
@@ -48,8 +63,9 @@ namespace Opc.Ua
|
||||
description,
|
||||
endpointConfiguration,
|
||||
clientCertificate,
|
||||
clientCertificateChain,
|
||||
messageContext);
|
||||
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
@@ -669,6 +669,27 @@ namespace Opc.Ua
|
||||
EndpointConfiguration endpointConfiguration,
|
||||
X509Certificate2 clientCertificate,
|
||||
ServiceMessageContext messageContext)
|
||||
{
|
||||
return CreateUaBinaryChannel(configuration, description, endpointConfiguration, clientCertificate, null, messageContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new UA-binary transport channel if requested. Null otherwise.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The application configuration.</param>
|
||||
/// <param name="description">The description for the endpoint.</param>
|
||||
/// <param name="endpointConfiguration">The configuration to use with the endpoint.</param>
|
||||
/// <param name="clientCertificate">The client certificate.</param>
|
||||
/// <param name="clientCertificateChain">The client certificate chain.</param>
|
||||
/// <param name="messageContext">The message context to use when serializing the messages.</param>
|
||||
/// <returns></returns>
|
||||
public static ITransportChannel CreateUaBinaryChannel(
|
||||
ApplicationConfiguration configuration,
|
||||
EndpointDescription description,
|
||||
EndpointConfiguration endpointConfiguration,
|
||||
X509Certificate2 clientCertificate,
|
||||
X509Certificate2Collection clientCertificateChain,
|
||||
ServiceMessageContext messageContext)
|
||||
{
|
||||
bool useUaTcp = description.EndpointUrl.StartsWith(Utils.UriSchemeOpcTcp);
|
||||
bool useHttps = description.EndpointUrl.StartsWith(Utils.UriSchemeHttps);
|
||||
@@ -693,7 +714,7 @@ namespace Opc.Ua
|
||||
if (!useUaTcp && !useHttps)
|
||||
{
|
||||
throw ServiceResultException.Create(
|
||||
StatusCodes.BadServiceUnsupported,
|
||||
StatusCodes.BadProtocolVersionUnsupported,
|
||||
"Unsupported transport profile\r\n");
|
||||
}
|
||||
|
||||
@@ -706,6 +727,7 @@ namespace Opc.Ua
|
||||
settings.Description = description;
|
||||
settings.Configuration = endpointConfiguration;
|
||||
settings.ClientCertificate = clientCertificate;
|
||||
settings.ClientCertificateChain = clientCertificateChain;
|
||||
|
||||
if (description.ServerCertificate != null && description.ServerCertificate.Length > 0)
|
||||
{
|
||||
|
||||
@@ -528,6 +528,22 @@ namespace Opc.Ua
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance certificate chain.
|
||||
/// </summary>
|
||||
protected X509Certificate2Collection InstanceCertificateChain
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_instanceCertificateChain;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
m_instanceCertificateChain = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The non-configurable properties for the server.
|
||||
/// </summary>
|
||||
@@ -737,6 +753,19 @@ namespace Opc.Ua
|
||||
if (requireEncryption)
|
||||
{
|
||||
description.ServerCertificate = InstanceCertificate.RawData;
|
||||
|
||||
// check if complete chain should be sent.
|
||||
if (configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count >0)
|
||||
{
|
||||
List<byte> serverCertificateChain = new List<byte>();
|
||||
|
||||
for (int i = 0; i < InstanceCertificateChain.Count; i++)
|
||||
{
|
||||
serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData);
|
||||
}
|
||||
|
||||
description.ServerCertificate = serverCertificateChain.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
endpoints.Add( description );
|
||||
@@ -749,10 +778,15 @@ namespace Opc.Ua
|
||||
|
||||
settings.Descriptions = endpoints;
|
||||
settings.Configuration = endpointConfiguration;
|
||||
settings.ServerCertificate = this.InstanceCertificate;
|
||||
settings.CertificateValidator = configuration.CertificateValidator.GetChannelValidator();
|
||||
settings.NamespaceUris = this.MessageContext.NamespaceUris;
|
||||
settings.Factory = this.MessageContext.Factory;
|
||||
settings.ServerCertificate = this.InstanceCertificate;
|
||||
|
||||
if (configuration.SecurityConfiguration.SendCertificateChain)
|
||||
{
|
||||
settings.ServerCertificateChain = this.InstanceCertificateChain;
|
||||
}
|
||||
|
||||
ITransportListener listener = new Opc.Ua.Bindings.UaTcpChannelListener();
|
||||
|
||||
@@ -855,6 +889,19 @@ namespace Opc.Ua
|
||||
if (InstanceCertificate != null)
|
||||
{
|
||||
description.ServerCertificate = InstanceCertificate.RawData;
|
||||
|
||||
// check if complete chain should be sent.
|
||||
if (configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count > 0)
|
||||
{
|
||||
List<byte> serverCertificateChain = new List<byte>();
|
||||
|
||||
for (int i = 0; i < InstanceCertificateChain.Count; i++)
|
||||
{
|
||||
serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData);
|
||||
}
|
||||
|
||||
description.ServerCertificate = serverCertificateChain.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
description.SecurityMode = bestPolicy.SecurityMode;
|
||||
@@ -1324,6 +1371,16 @@ namespace Opc.Ua
|
||||
"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();
|
||||
|
||||
@@ -1487,6 +1544,7 @@ namespace Opc.Ua
|
||||
private object m_serverError;
|
||||
private object m_certificateValidator;
|
||||
private object m_instanceCertificate;
|
||||
private X509Certificate2Collection m_instanceCertificateChain;
|
||||
private object m_serverProperties;
|
||||
private object m_configuration;
|
||||
private object m_serverDescription;
|
||||
|
||||
@@ -344,6 +344,7 @@ namespace Opc.Ua.Bindings
|
||||
m_bufferManager,
|
||||
m_quotas,
|
||||
m_serverCertificate,
|
||||
m_serverCertificateChain,
|
||||
m_descriptions);
|
||||
|
||||
if (m_callback != null)
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace Opc.Ua.Bindings
|
||||
/// </summary>
|
||||
public class TcpServerChannel : UaSCUaBinaryChannel
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Attaches the object to an existing socket.
|
||||
/// </summary>
|
||||
@@ -38,7 +37,24 @@ namespace Opc.Ua.Bindings
|
||||
X509Certificate2 serverCertificate,
|
||||
EndpointDescriptionCollection endpoints)
|
||||
:
|
||||
base(contextId, bufferManager, quotas, serverCertificate, endpoints, MessageSecurityMode.None, SecurityPolicies.None)
|
||||
this(contextId, listener, bufferManager, quotas, serverCertificate, null, endpoints)
|
||||
{
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Attaches the object to an existing socket.
|
||||
/// </summary>
|
||||
public TcpServerChannel(
|
||||
string contextId,
|
||||
UaTcpChannelListener listener,
|
||||
BufferManager bufferManager,
|
||||
ChannelQuotas quotas,
|
||||
X509Certificate2 serverCertificate,
|
||||
X509Certificate2Collection serverCertificateChain,
|
||||
EndpointDescriptionCollection endpoints)
|
||||
:
|
||||
base(contextId, bufferManager, quotas, serverCertificate, serverCertificateChain, endpoints, MessageSecurityMode.None, SecurityPolicies.None)
|
||||
{
|
||||
m_listener = listener;
|
||||
m_queuedResponses = new SortedDictionary<uint, IServiceResponse>();
|
||||
@@ -135,7 +151,7 @@ namespace Opc.Ua.Bindings
|
||||
// need to assign a new token id.
|
||||
token.TokenId = GetNewTokenId();
|
||||
|
||||
// put channel back in open state.
|
||||
// put channel back in open state.
|
||||
ActivateToken(token);
|
||||
State = TcpChannelState.Open;
|
||||
|
||||
@@ -735,7 +751,7 @@ namespace Opc.Ua.Bindings
|
||||
/// </summary>
|
||||
private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment<byte> messageChunk)
|
||||
{
|
||||
// validate the channel state.
|
||||
// validate the channel state.
|
||||
if (State != TcpChannelState.Opening && State != TcpChannelState.Open)
|
||||
{
|
||||
ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected OpenSecureChannel message.");
|
||||
@@ -984,6 +1000,7 @@ namespace Opc.Ua.Bindings
|
||||
TcpMessageType.Open,
|
||||
requestId,
|
||||
ServerCertificate,
|
||||
ServerCertificateChain,
|
||||
ClientCertificate,
|
||||
new ArraySegment<byte>(buffer, 0, buffer.Length));
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Text;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Cryptography;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Opc.Ua.Bindings
|
||||
{
|
||||
@@ -56,6 +57,15 @@ namespace Opc.Ua.Bindings
|
||||
get { return m_serverCertificate; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The server certificate chain.
|
||||
/// </summary>
|
||||
protected X509Certificate2Collection ServerCertificateChain
|
||||
{
|
||||
get { return m_serverCertificateChain; }
|
||||
set { m_serverCertificateChain = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The security mode used with the channel.
|
||||
/// </summary>
|
||||
@@ -92,6 +102,14 @@ namespace Opc.Ua.Bindings
|
||||
set { m_clientCertificate = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The client certificate chain.
|
||||
/// </summary>
|
||||
internal X509Certificate2Collection ClientCertificateChain
|
||||
{
|
||||
get { return m_clientCertificateChain; }
|
||||
set { m_clientCertificateChain = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new nonce.
|
||||
@@ -399,8 +417,35 @@ namespace Opc.Ua.Bindings
|
||||
string securityPolicyUri,
|
||||
X509Certificate2 senderCertificate,
|
||||
X509Certificate2 receiverCertificate)
|
||||
{
|
||||
int senderCertificateSize = 0;
|
||||
|
||||
WriteAsymmetricMessageHeader(
|
||||
encoder,
|
||||
messageType,
|
||||
secureChannelId,
|
||||
securityPolicyUri,
|
||||
senderCertificate,
|
||||
null,
|
||||
receiverCertificate,
|
||||
out senderCertificateSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the asymmetric security header to the buffer.
|
||||
/// </summary>
|
||||
protected void WriteAsymmetricMessageHeader(
|
||||
BinaryEncoder encoder,
|
||||
uint messageType,
|
||||
uint secureChannelId,
|
||||
string securityPolicyUri,
|
||||
X509Certificate2 senderCertificate,
|
||||
X509Certificate2Collection senderCertificateChain,
|
||||
X509Certificate2 receiverCertificate,
|
||||
out int senderCertificateSize)
|
||||
{
|
||||
int start = encoder.Position;
|
||||
senderCertificateSize = 0;
|
||||
|
||||
encoder.WriteUInt32(null, messageType);
|
||||
encoder.WriteUInt32(null, 0);
|
||||
@@ -409,7 +454,36 @@ namespace Opc.Ua.Bindings
|
||||
|
||||
if (SecurityMode != MessageSecurityMode.None)
|
||||
{
|
||||
encoder.WriteByteString(null, senderCertificate.RawData);
|
||||
if (senderCertificateChain != null && senderCertificateChain.Count >0)
|
||||
{
|
||||
X509Certificate2 currentCertificate = senderCertificateChain[0];
|
||||
int maxSenderCertificateSize = GetMaxSenderCertificateSize(currentCertificate, securityPolicyUri);
|
||||
List<byte> senderCertificateList = new List<byte>(currentCertificate.RawData);
|
||||
senderCertificateSize = currentCertificate.RawData.Length;
|
||||
|
||||
for (int i = 1; i < senderCertificateChain.Count; i++)
|
||||
{
|
||||
currentCertificate = senderCertificateChain[i];
|
||||
senderCertificateSize += currentCertificate.RawData.Length;
|
||||
|
||||
if (senderCertificateSize < maxSenderCertificateSize)
|
||||
{
|
||||
senderCertificateList.AddRange(currentCertificate.RawData);
|
||||
}
|
||||
else
|
||||
{
|
||||
senderCertificateSize -= currentCertificate.RawData.Length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
encoder.WriteByteString(null, senderCertificateList.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
encoder.WriteByteString(null, senderCertificate.RawData);
|
||||
}
|
||||
|
||||
encoder.WriteByteString(null, GetThumbprintBytes(receiverCertificate.Thumbprint));
|
||||
}
|
||||
else
|
||||
@@ -452,7 +526,7 @@ namespace Opc.Ua.Bindings
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a OpenSecureChannel response.
|
||||
/// Sends a OpenSecureChannel request.
|
||||
/// </summary>
|
||||
protected BufferCollection WriteAsymmetricMessage(
|
||||
uint messageType,
|
||||
@@ -460,6 +534,20 @@ namespace Opc.Ua.Bindings
|
||||
X509Certificate2 senderCertificate,
|
||||
X509Certificate2 receiverCertificate,
|
||||
ArraySegment<byte> messageBody)
|
||||
{
|
||||
return WriteAsymmetricMessage(messageType, requestId, senderCertificate, null, receiverCertificate, messageBody);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a OpenSecureChannel request.
|
||||
/// </summary>
|
||||
protected BufferCollection WriteAsymmetricMessage(
|
||||
uint messageType,
|
||||
uint requestId,
|
||||
X509Certificate2 senderCertificate,
|
||||
X509Certificate2Collection senderCertificateChain,
|
||||
X509Certificate2 receiverCertificate,
|
||||
ArraySegment<byte> messageBody)
|
||||
{
|
||||
bool success = false;
|
||||
BufferCollection chunksToSend = new BufferCollection();
|
||||
@@ -468,18 +556,39 @@ namespace Opc.Ua.Bindings
|
||||
|
||||
try
|
||||
{
|
||||
int headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate);
|
||||
int signatureSize = GetAsymmetricSignatureSize(senderCertificate);
|
||||
|
||||
BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext);
|
||||
int headerSize = 0;
|
||||
|
||||
WriteAsymmetricMessageHeader(
|
||||
encoder,
|
||||
messageType | TcpMessageType.Intermediate,
|
||||
ChannelId,
|
||||
SecurityPolicyUri,
|
||||
senderCertificate,
|
||||
receiverCertificate);
|
||||
if (senderCertificateChain != null && senderCertificateChain.Count >0)
|
||||
{
|
||||
int senderCertificateSize = 0;
|
||||
|
||||
WriteAsymmetricMessageHeader(
|
||||
encoder,
|
||||
messageType | TcpMessageType.Intermediate,
|
||||
ChannelId,
|
||||
SecurityPolicyUri,
|
||||
senderCertificate,
|
||||
senderCertificateChain,
|
||||
receiverCertificate,
|
||||
out senderCertificateSize);
|
||||
|
||||
headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate, senderCertificateSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteAsymmetricMessageHeader(
|
||||
encoder,
|
||||
messageType | TcpMessageType.Intermediate,
|
||||
ChannelId,
|
||||
SecurityPolicyUri,
|
||||
senderCertificate,
|
||||
receiverCertificate);
|
||||
|
||||
headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate);
|
||||
}
|
||||
|
||||
int signatureSize = GetAsymmetricSignatureSize(senderCertificate);
|
||||
|
||||
// save the header.
|
||||
ArraySegment<byte> header = new ArraySegment<byte>(buffer, 0, headerSize);
|
||||
@@ -637,11 +746,11 @@ namespace Opc.Ua.Bindings
|
||||
protected void ReadAsymmetricMessageHeader(
|
||||
BinaryDecoder decoder,
|
||||
X509Certificate2 receiverCertificate,
|
||||
out uint secureChannelId,
|
||||
out X509Certificate2 senderCertificate,
|
||||
out uint secureChannelId,
|
||||
out X509Certificate2Collection senderCertificateChain,
|
||||
out string securityPolicyUri)
|
||||
{
|
||||
senderCertificate = null;
|
||||
senderCertificateChain = null;
|
||||
|
||||
uint messageType = decoder.ReadUInt32(null);
|
||||
uint messageSize = decoder.ReadUInt32(null);
|
||||
@@ -665,14 +774,14 @@ namespace Opc.Ua.Bindings
|
||||
"The asymmetric security header could not be parsed.");
|
||||
}
|
||||
|
||||
// verify sender certificate.
|
||||
// verify sender certificate chain.
|
||||
if (certificateData != null && certificateData.Length > 0)
|
||||
{
|
||||
senderCertificate = CertificateFactory.Create(certificateData, true);
|
||||
senderCertificateChain = Utils.ParseCertificateChainBlob(certificateData);
|
||||
|
||||
try
|
||||
{
|
||||
string thumbprint = senderCertificate.Thumbprint;
|
||||
string thumbprint = senderCertificateChain[0].Thumbprint;
|
||||
|
||||
if (thumbprint == null)
|
||||
{
|
||||
@@ -790,19 +899,38 @@ namespace Opc.Ua.Bindings
|
||||
BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext);
|
||||
|
||||
string securityPolicyUri = null;
|
||||
X509Certificate2Collection senderCertificateChain;
|
||||
|
||||
// parse the security header.
|
||||
ReadAsymmetricMessageHeader(
|
||||
decoder,
|
||||
receiverCertificate,
|
||||
out channelId,
|
||||
out senderCertificate,
|
||||
out senderCertificateChain,
|
||||
out securityPolicyUri);
|
||||
|
||||
if (senderCertificateChain != null && senderCertificateChain.Count > 0)
|
||||
{
|
||||
senderCertificate = senderCertificateChain[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
senderCertificate = null;
|
||||
}
|
||||
|
||||
// validate the sender certificate.
|
||||
if (senderCertificate != null && Quotas.CertificateValidator != null && securityPolicyUri != SecurityPolicies.None)
|
||||
{
|
||||
Quotas.CertificateValidator.Validate(senderCertificate);
|
||||
CertificateValidator certificateValidator = Quotas.CertificateValidator as CertificateValidator;
|
||||
|
||||
if (certificateValidator != null)
|
||||
{
|
||||
certificateValidator.Validate(senderCertificateChain);
|
||||
}
|
||||
else
|
||||
{
|
||||
Quotas.CertificateValidator.Validate(senderCertificate);
|
||||
}
|
||||
}
|
||||
|
||||
// check if this is the first open secure channel request.
|
||||
@@ -1095,7 +1223,9 @@ namespace Opc.Ua.Bindings
|
||||
private bool m_discoveryOnly;
|
||||
private EndpointDescription m_selectedEndpoint;
|
||||
private X509Certificate2 m_serverCertificate;
|
||||
private X509Certificate2Collection m_serverCertificateChain;
|
||||
private X509Certificate2 m_clientCertificate;
|
||||
private X509Certificate2Collection m_clientCertificateChain;
|
||||
private bool m_uninitialized;
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Opc.Ua.Bindings
|
||||
public partial class UaSCUaBinaryChannel : IMessageSink, IDisposable
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Attaches the object to an existing socket.
|
||||
/// </summary>
|
||||
@@ -35,6 +36,22 @@ namespace Opc.Ua.Bindings
|
||||
EndpointDescriptionCollection endpoints,
|
||||
MessageSecurityMode securityMode,
|
||||
string securityPolicyUri)
|
||||
:
|
||||
this(contextId, bufferManager, quotas, serverCertificate, null, endpoints, securityMode, securityPolicyUri)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Attaches the object to an existing socket.
|
||||
/// </summary>
|
||||
public UaSCUaBinaryChannel(
|
||||
string contextId,
|
||||
BufferManager bufferManager,
|
||||
ChannelQuotas quotas,
|
||||
X509Certificate2 serverCertificate,
|
||||
X509Certificate2Collection serverCertificateChain,
|
||||
EndpointDescriptionCollection endpoints,
|
||||
MessageSecurityMode securityMode,
|
||||
string securityPolicyUri)
|
||||
{
|
||||
if (bufferManager == null) throw new ArgumentNullException("bufferManager");
|
||||
if (quotas == null) throw new ArgumentNullException("quotas");
|
||||
@@ -75,6 +92,7 @@ namespace Opc.Ua.Bindings
|
||||
m_bufferManager = bufferManager;
|
||||
m_quotas = quotas;
|
||||
m_serverCertificate = serverCertificate;
|
||||
m_serverCertificateChain = serverCertificateChain;
|
||||
m_endpoints = endpoints;
|
||||
m_securityMode = securityMode;
|
||||
m_securityPolicyUri = securityPolicyUri;
|
||||
|
||||
@@ -39,6 +39,23 @@ namespace Opc.Ua.Bindings
|
||||
X509Certificate2 clientCertificate,
|
||||
X509Certificate2 serverCertificate,
|
||||
EndpointDescription endpoint)
|
||||
:
|
||||
this(contextId, bufferManager, socketFactory, quotas, clientCertificate, null, serverCertificate, endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a channel for for a client.
|
||||
/// </summary>
|
||||
public UaSCUaBinaryClientChannel(
|
||||
string contextId,
|
||||
BufferManager bufferManager,
|
||||
IMessageSocketFactory socketFactory,
|
||||
ChannelQuotas quotas,
|
||||
X509Certificate2 clientCertificate,
|
||||
X509Certificate2Collection clientCertificateChain,
|
||||
X509Certificate2 serverCertificate,
|
||||
EndpointDescription endpoint)
|
||||
:
|
||||
base(
|
||||
contextId,
|
||||
@@ -61,6 +78,7 @@ namespace Opc.Ua.Bindings
|
||||
}
|
||||
|
||||
ClientCertificate = clientCertificate;
|
||||
ClientCertificateChain = clientCertificateChain;
|
||||
}
|
||||
|
||||
m_requests = new Dictionary<uint, WriteOperation>();
|
||||
@@ -471,7 +489,7 @@ namespace Opc.Ua.Bindings
|
||||
request.ClientNonce = token.ClientNonce;
|
||||
request.RequestedLifetime = (uint)Quotas.SecurityTokenLifetime;
|
||||
|
||||
// encode the request.
|
||||
// encode the request.
|
||||
byte[] buffer = BinaryEncoder.EncodeMessage(request, Quotas.MessageContext);
|
||||
|
||||
// write the asymmetric message.
|
||||
@@ -479,6 +497,7 @@ namespace Opc.Ua.Bindings
|
||||
TcpMessageType.Open,
|
||||
m_handshakeOperation.RequestId,
|
||||
ClientCertificate,
|
||||
ClientCertificateChain,
|
||||
ServerCertificate,
|
||||
new ArraySegment<byte>(buffer, 0, buffer.Length));
|
||||
|
||||
|
||||
@@ -144,6 +144,7 @@ namespace Opc.Ua.Bindings
|
||||
m_messageSocketFactory,
|
||||
m_quotas,
|
||||
m_settings.ClientCertificate,
|
||||
m_settings.ClientCertificateChain,
|
||||
m_settings.ServerCertificate,
|
||||
m_settings.Description);
|
||||
|
||||
@@ -389,6 +390,7 @@ namespace Opc.Ua.Bindings
|
||||
m_messageSocketFactory,
|
||||
m_quotas,
|
||||
m_settings.ClientCertificate,
|
||||
m_settings.ClientCertificateChain,
|
||||
m_settings.ServerCertificate,
|
||||
m_settings.Description);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Opc.Ua
|
||||
if (certificate == null)
|
||||
{
|
||||
certificate = CertificateFactory.Create(m_certificateData, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool valid = SecurityPolicies.Verify(
|
||||
certificate,
|
||||
|
||||
@@ -2585,7 +2585,6 @@ namespace Opc.Ua
|
||||
certificatesBytes.RemoveRange(0, certificate.RawData.Length);
|
||||
}
|
||||
|
||||
|
||||
return certificateChain;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user