public void Open(
string sessionName,
uint sessionTimeout,
IUserIdentity identity,
IList<string> preferredLocales,
bool checkDomain)
{
// check connection state.
lock (SyncRoot)
{
if (Connected)
{
throw new ServiceResultException(StatusCodes.BadInvalidState, "Already connected to server.");
}
}
string securityPolicyUri = m_endpoint.Description.SecurityPolicyUri;
// get the identity token.
if (identity == null)
{
identity = new UserIdentity();
}
// get identity token.
UserIdentityToken identityToken = identity.GetIdentityToken();
// check that the user identity is supported by the endpoint.
UserTokenPolicy identityPolicy = m_endpoint.Description.FindUserTokenPolicy(identityToken.PolicyId);
if (identityPolicy == null)
{
// try looking up by TokenType if the policy id was not found.
identityPolicy = m_endpoint.Description.FindUserTokenPolicy(identity.TokenType, identity.IssuedTokenType);
if (identityPolicy == null)
{
throw ServiceResultException.Create(
StatusCodes.BadUserAccessDenied,
"Endpoint does not supported the user identity type provided.");
}
identityToken.PolicyId = identityPolicy.PolicyId;
}
bool requireEncryption = securityPolicyUri != SecurityPolicies.None;
if (!requireEncryption)
{
requireEncryption = identityPolicy.SecurityPolicyUri != SecurityPolicies.None &&
!String.IsNullOrEmpty(identityPolicy.SecurityPolicyUri);
}
// validate the server certificate.
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);
if(checkDomain)
{
CheckCertificateDomain(m_endpoint);
}
//X509Certificate2Collection certificateChain = Utils.ParseCertificateChainBlob(certificateData);
//if (certificateChain.Count > 0)
// serverCertificate = certificateChain[0];
//m_configuration.CertificateValidator.Validate(certificateChain);
}
// create a nonce.
byte[] clientNonce = new byte[m_configuration.SecurityConfiguration.NonceLength];
new RNGCryptoServiceProvider().GetBytes(clientNonce);
NodeId sessionId = null;
NodeId sessionCookie = null;
byte[] serverNonce = new byte[0];
byte[] serverCertificateData = new byte[0];
SignatureData serverSignature = null;
EndpointDescriptionCollection serverEndpoints = null;
SignedSoftwareCertificateCollection serverSoftwareCertificates = null;
// send the application instance certificate for the client.
byte[] clientCertificateData = m_instanceCertificate != null ? m_instanceCertificate.RawData : null;
//byte[] clientCertificateData = null;
//if (m_instanceCertificateChain != null)
//{
// List<byte> clientCertificateChainData = new List<byte>();
// for (int i = 0; i < m_instanceCertificateChain.Count; i++)
// {
// clientCertificateChainData.AddRange(m_instanceCertificateChain[i].RawData);
// }
// clientCertificateData = clientCertificateChainData.ToArray();
//}
ApplicationDescription clientDescription = new ApplicationDescription();
clientDescription.ApplicationUri = m_configuration.ApplicationUri;
clientDescription.ApplicationName = m_configuration.ApplicationName;
clientDescription.ApplicationType = ApplicationType.Client;
clientDescription.ProductUri = m_configuration.ProductUri;
if (sessionTimeout == 0)
{
sessionTimeout = (uint)m_configuration.ClientConfiguration.DefaultSessionTimeout;
}
bool successCreateSession = false;
//if security none, first try to connect without certificate
if (m_endpoint.Description.SecurityPolicyUri == SecurityPolicies.None)
{
//first try to connect with client certificate NULL
try
{
CreateSession(
null,
clientDescription,
m_endpoint.Description.Server.ApplicationUri,
m_endpoint.EndpointUrl.ToString(),
sessionName,
clientNonce,
null,
sessionTimeout,
(uint)MessageContext.MaxMessageSize,
out sessionId,
out sessionCookie,
out m_sessionTimeout,
out serverNonce,
out serverCertificateData,
out serverEndpoints,
out serverSoftwareCertificates,
out serverSignature,
out m_maxRequestMessageSize);
successCreateSession = true;
}
catch(Exception ex)
{
Utils.Trace("Create session failed with client certificate NULL. " + ex.Message);
successCreateSession = false;
}
}
if (!successCreateSession)
{
CreateSession(
null,
clientDescription,
m_endpoint.Description.Server.ApplicationUri,
m_endpoint.EndpointUrl.ToString(),
sessionName,
clientNonce,
clientCertificateData,
sessionTimeout,
(uint)MessageContext.MaxMessageSize,
out sessionId,
out sessionCookie,
out m_sessionTimeout,
out serverNonce,
out serverCertificateData,
out serverEndpoints,
out serverSoftwareCertificates,
out serverSignature,
out m_maxRequestMessageSize);
}
// save session id.
lock (SyncRoot)
{
base.SessionCreated(sessionId, sessionCookie);
}
Utils.Trace("Revised session timeout value: {0}. ", m_sessionTimeout);
Utils.Trace("Max response message size value: {0}. Max request message size: {1} ", MessageContext.MaxMessageSize, m_maxRequestMessageSize);
//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." );
}
if (serverSignature == null || serverSignature.Signature == null)
{
Utils.Trace("Server signature is null or empty.");
//throw ServiceResultException.Create(
// StatusCodes.BadSecurityChecksFailed,
// "Server signature is null or empty.");
}
if (m_expectedServerEndpoints != null && m_expectedServerEndpoints.Count > 0)
{
// verify that the list of endpoints returned by CreateSession matches the list returned at GetEndpoints.
if (m_expectedServerEndpoints.Count != serverEndpoints.Count)
{
throw ServiceResultException.Create(
StatusCodes.BadSecurityChecksFailed,
"Server did not return a number of ServerEndpoints that matches the one from GetEndpoints.");
}
for (int ii = 0; ii < serverEndpoints.Count; ii++)
{
EndpointDescription serverEndpoint = serverEndpoints[ii];
EndpointDescription expectedServerEndpoint = m_expectedServerEndpoints[ii];
if (serverEndpoint.SecurityMode != expectedServerEndpoint.SecurityMode ||
serverEndpoint.SecurityPolicyUri != expectedServerEndpoint.SecurityPolicyUri ||
serverEndpoint.TransportProfileUri != expectedServerEndpoint.TransportProfileUri ||
serverEndpoint.SecurityLevel != expectedServerEndpoint.SecurityLevel)
{
throw ServiceResultException.Create(
StatusCodes.BadSecurityChecksFailed,
"The list of ServerEndpoints returned at CreateSession does not match the list from GetEndpoints.");
}
if (serverEndpoint.UserIdentityTokens.Count != expectedServerEndpoint.UserIdentityTokens.Count)
{
throw ServiceResultException.Create(
StatusCodes.BadSecurityChecksFailed,
"The list of ServerEndpoints returned at CreateSession does not match the one from GetEndpoints.");
}
for (int jj = 0; jj < serverEndpoint.UserIdentityTokens.Count; jj++)
{
if (!serverEndpoint.UserIdentityTokens[jj].IsEqual(expectedServerEndpoint.UserIdentityTokens[jj]))
{
throw ServiceResultException.Create(
StatusCodes.BadSecurityChecksFailed,
"The list of ServerEndpoints returned at CreateSession does not match the one from GetEndpoints.");
}
}
}
}
// find the matching description (TBD - check domains against certificate).
bool found = false;
Uri expectedUrl = Utils.ParseUri( m_endpoint.Description.EndpointUrl );
if (expectedUrl != null)
{
for (int ii = 0; ii < serverEndpoints.Count; ii++)
{
EndpointDescription serverEndpoint = serverEndpoints[ii];
Uri actualUrl = Utils.ParseUri(serverEndpoint.EndpointUrl);
if (actualUrl != null && actualUrl.Scheme == expectedUrl.Scheme)
{
if (serverEndpoint.SecurityPolicyUri == m_endpoint.Description.SecurityPolicyUri)
{
if (serverEndpoint.SecurityMode == m_endpoint.Description.SecurityMode)
{
// ensure endpoint has up to date information.
m_endpoint.Description.Server.ApplicationName = serverEndpoint.Server.ApplicationName;
m_endpoint.Description.Server.ApplicationUri = serverEndpoint.Server.ApplicationUri;
m_endpoint.Description.Server.ApplicationType = serverEndpoint.Server.ApplicationType;
m_endpoint.Description.Server.ProductUri = serverEndpoint.Server.ProductUri;
m_endpoint.Description.TransportProfileUri = serverEndpoint.TransportProfileUri;
m_endpoint.Description.UserIdentityTokens = serverEndpoint.UserIdentityTokens;
found = true;
break;
}
}
}
}
}
// could be a security risk.
if( !found )
{
throw ServiceResultException.Create(
StatusCodes.BadSecurityChecksFailed,
"Server did not return an EndpointDescription that matched the one used to create the secure channel." );
}
// validate the server's signature.
byte[] dataToSign = Utils.Append( clientCertificateData, 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." );
}
// get a validator to check certificates provided by server.
CertificateValidator validator = m_configuration.CertificateValidator;
// validate software certificates.
List<SoftwareCertificate> softwareCertificates = new List<SoftwareCertificate>();
foreach( SignedSoftwareCertificate signedCertificate in serverSoftwareCertificates )
{
SoftwareCertificate softwareCertificate = null;
ServiceResult result = SoftwareCertificate.Validate(
validator,
signedCertificate.CertificateData,
out softwareCertificate );
if( ServiceResult.IsBad( result ) )
{
OnSoftwareCertificateError( signedCertificate, result );
}
softwareCertificates.Add( softwareCertificate );
}
// check if software certificates meet application requirements.
ValidateSoftwareCertificates( softwareCertificates );
// create the client signature.
dataToSign = Utils.Append( serverCertificateData, serverNonce );
SignatureData clientSignature = SecurityPolicies.Sign( m_instanceCertificate, securityPolicyUri, dataToSign );
// select the security policy for the user token.
securityPolicyUri = identityPolicy.SecurityPolicyUri;
if (String.IsNullOrEmpty(securityPolicyUri))
{
securityPolicyUri = m_endpoint.Description.SecurityPolicyUri;
}
// sign data with user token.
SignatureData userTokenSignature = identityToken.Sign( dataToSign, securityPolicyUri );
// encrypt token.
identityToken.Encrypt( serverCertificate, serverNonce, securityPolicyUri );
// send the software certificates assigned to the client.
SignedSoftwareCertificateCollection clientSoftwareCertificates = GetSoftwareCertificates();
// copy the preferred locales if provided.
if( preferredLocales != null && preferredLocales.Count > 0 )
{
m_preferredLocales = new StringCollection( preferredLocales );
}
StatusCodeCollection certificateResults = null;
DiagnosticInfoCollection certificateDiagnosticInfos = null;
// activate session.
ActivateSession(
null,
clientSignature,
clientSoftwareCertificates,
m_preferredLocales,
new ExtensionObject( identityToken ),
userTokenSignature,
out serverNonce,
out certificateResults,
out certificateDiagnosticInfos );
if (certificateResults != null)
{
for (int i = 0; i < certificateResults.Count; i++)
{
Utils.Trace("ActivateSession result[{0}] = {1}", i, certificateResults[i]);
}
}
if (certificateResults == null || certificateResults.Count == 0)
{
Utils.Trace("Empty results were received for the ActivateSession call.");
}
// fetch namespaces.
FetchNamespaceTables();
lock( SyncRoot )
{
// save nonces.
m_sessionName = sessionName;
m_identity = identity;
m_serverNonce = serverNonce;
m_serverCertificate = serverCertificate;
// update system context.
m_systemContext.PreferredLocales = m_preferredLocales;
m_systemContext.SessionId = this.SessionId;
m_systemContext.UserIdentity = identity;
}
// start keep alive thread.
StartKeepAliveTimer();
}
catch
{
try
{
CloseSession( null, false );
CloseChannel();
}
catch(Exception e)
{
Utils.Trace("Cleanup: CloseSession() or CloseChannel() raised exception. " + e.Message);
}
finally
{
SessionCreated( null, null );
}
throw;
}
}