public static LogEntry For(OpenSecureChannelRequest request) { LogEntry entry = new LogEntry("OpenSecureChannelRequest"); entry.Add("RequestHeader", For(request.RequestHeader)); entry.Add("SecurityMode", For(request.SecurityMode)); return(entry); }
/// <summary> /// Handles a reconnect request. /// </summary> public void Reconnect( TcpMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { if (socket == null) { throw new ArgumentNullException("socket"); } lock (DataLock) { // make sure the same client certificate is being used. CompareCertificates(ClientCertificate, clientCertificate, false); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "Reconnect")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } try { // replace the socket. Socket = socket; Utils.Trace("TCPSERVERCHANNEL SOCKET RECONNECTED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId); Socket.ChangeSink(this); // need to assign a new token id. token.TokenId = GetNewTokenId(); // put channel back in open state. ActivateToken(token); State = TcpChannelState.Open; // no need to cleanup. if (m_cleanupTimer != null) { m_cleanupTimer.Dispose(); m_cleanupTimer = null; } // send response. SendOpenSecureChannelResponse(requestId, token, request); // send any queue responses. ThreadPool.QueueUserWorkItem(new WaitCallback(OnChannelReconnected), m_queuedResponses); m_queuedResponses = new SortedDictionary <uint, IServiceResponse>(); } catch (Exception e) { SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); } } }
/// <inheritdoc/> public void ReportAuditOpenSecureChannelEvent( string globalChannelId, EndpointDescription endpointDescription, OpenSecureChannelRequest request, X509Certificate2 clientCertificate, Exception exception) { // trigger the reporting of AuditOpenSecureChannelEventType ServerForContext?.ReportAuditOpenSecureChannelEvent(globalChannelId, endpointDescription, request, clientCertificate, exception); }
/// <summary> /// Binds a new socket to an existing channel. /// </summary> public bool ReconnectToExistingChannel( IMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { return(true); }
/// <summary> /// Handles a reconnect request. /// </summary> public override void Reconnect( IMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { if (socket == null) { throw new ArgumentNullException(nameof(socket)); } lock (DataLock) { // make sure the same client certificate is being used. CompareCertificates(ClientCertificate, clientCertificate, false); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "Reconnect")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } try { // replace the socket. Socket = socket; Utils.Trace("{0} SOCKET RECONNECTED: {1:X8}, ChannelId={2}", ChannelName, Socket.Handle, ChannelId); Socket.ChangeSink(this); // need to assign a new token id. token.TokenId = GetNewTokenId(); // put channel back in open state. ActivateToken(token); State = TcpChannelState.Open; // no need to cleanup. CleanupTimer(); // send response. SendOpenSecureChannelResponse(requestId, token, request); // send any queue responses. Task.Factory.StartNew(OnChannelReconnected, m_queuedResponses); m_queuedResponses = new SortedDictionary <uint, IServiceResponse>(); } catch (Exception e) { SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); } } }
/// <summary> /// Send open secure channel service request on transport channel. /// </summary> /// <param name="request">A service request</param> /// <param name="token">A cancellation token</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> private async Task SendOpenSecureChannelRequestAsync(OpenSecureChannelRequest request, CancellationToken token) { var bodyStream = _streamManager.GetStream("SendOpenSecureChannelRequestAsync"); using (var bodyEncoder = StackProfile.EncodingProvider.CreateEncoder(bodyStream, this, keepStreamOpen: false)) { bodyEncoder.WriteRequest(request); bodyStream.Position = 0; var handle = request.RequestHeader !.RequestHandle; await _conversation !.EncryptMessageAsync(bodyStream, MessageTypes.OPNF, handle, SendAsync, token); } }
/// <summary> /// Sends an OpenSecureChannel response. /// </summary> private void SendOpenSecureChannelResponse(uint requestId, TcpChannelToken token, OpenSecureChannelRequest request) { // Utils.Trace("Channel {0}: SendOpenSecureChannelResponse()", ChannelId); OpenSecureChannelResponse response = new OpenSecureChannelResponse(); response.ResponseHeader.RequestHandle = request.RequestHeader.RequestHandle; response.ResponseHeader.Timestamp = DateTime.UtcNow; response.SecurityToken.ChannelId = token.ChannelId; response.SecurityToken.TokenId = token.TokenId; response.SecurityToken.CreatedAt = token.CreatedAt; response.SecurityToken.RevisedLifetime = (uint)token.Lifetime; response.ServerNonce = token.ServerNonce; byte[] buffer = BinaryEncoder.EncodeMessage(response, Quotas.MessageContext); BufferCollection chunksToSend = null; if (ServerCertificateChain != null) { chunksToSend = WriteAsymmetricMessage( TcpMessageType.Open, requestId, ServerCertificateChain, //ServerCertificate, ClientCertificate, new ArraySegment <byte>(buffer, 0, buffer.Length)); } else { chunksToSend = WriteAsymmetricMessage( TcpMessageType.Open, requestId, //ServerCertificateChain, ServerCertificate, ClientCertificate, new ArraySegment <byte>(buffer, 0, buffer.Length)); } // write the message to the server. try { BeginWriteMessage(chunksToSend, Int32.MaxValue, null); chunksToSend = null; } finally { if (chunksToSend != null) { chunksToSend.Release(BufferManager, "SendOpenSecureChannelResponse"); } } }
/// <summary> /// Callback for reporting the open secure channel audit event /// </summary> private void OnReportAuditOpenSecureChannelEvent(TcpServerChannel channel, OpenSecureChannelRequest request, X509Certificate2 clientCertificate, Exception exception) { try { if (m_callback != null) { m_callback.ReportAuditOpenSecureChannelEvent(channel.GlobalChannelId, channel.EndpointDescription, request, clientCertificate, exception); } } catch (Exception e) { Utils.LogError(e, "TCPLISTENER - Unexpected error sending OpenSecureChannel Audit event."); } }
/// <inheritdoc/> public bool ReconnectToExistingChannel(IMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { if (!_channels.TryGetValue(channelId, out var channel)) { throw ServiceResultException.Create(Opc.Ua.StatusCodes.BadTcpSecureChannelUnknown, "Could not find channel referenced in the OpenSecureChannel request."); } channel.Reconnect(socket, requestId, sequenceNumber, clientCertificate, token, request); _logger.Information("Channel {channelId} reconnected", channelId); return(true); }
/// <summary> /// Binds a new socket to an existing channel. /// </summary> internal bool ReconnectToExistingChannel( TcpMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { TcpServerChannel channel = null; lock (m_lock) { if (!m_channels.TryGetValue(channelId, out channel)) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Could not find secure channel referenced in the OpenSecureChannel request."); } } channel.Reconnect(socket, requestId, sequenceNumber, clientCertificate, token, request); return(true); }
/// <summary> /// Sends an OpenSecureChannel response. /// </summary> private void SendOpenSecureChannelResponse(uint requestId, TcpChannelToken token, OpenSecureChannelRequest request) { // Utils.Trace("Channel {0}: SendOpenSecureChannelResponse()", ChannelId); OpenSecureChannelResponse response = new OpenSecureChannelResponse(); response.ResponseHeader.RequestHandle = request.RequestHeader.RequestHandle; response.ResponseHeader.Timestamp = DateTime.UtcNow; response.SecurityToken.ChannelId = token.ChannelId; response.SecurityToken.TokenId = token.TokenId; response.SecurityToken.CreatedAt = token.CreatedAt; response.SecurityToken.RevisedLifetime = (uint)token.Lifetime; response.ServerNonce = token.ServerNonce; byte[] buffer = BinaryEncoder.EncodeMessage(response, Quotas.MessageContext); BufferCollection chunksToSend = WriteAsymmetricMessage( TcpMessageType.Open, requestId, //ServerCertificateChain, ServerCertificate, ClientCertificate, new ArraySegment<byte>(buffer, 0, buffer.Length)); // write the message to the server. try { BeginWriteMessage(chunksToSend, Int32.MaxValue, null); chunksToSend = null; } finally { if (chunksToSend != null) { chunksToSend.Release(BufferManager, "SendOpenSecureChannelResponse"); } } }
/// <summary> /// Handles a reconnect request. /// </summary> public void Reconnect( TcpMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { if (socket == null) throw new ArgumentNullException("socket"); lock (DataLock) { // make sure the same client certificate is being used. CompareCertificates(ClientCertificate, clientCertificate, false); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "Reconnect")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } try { // replace the socket. Socket = socket; Utils.Trace("TCPSERVERCHANNEL SOCKET RECONNECTED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId); Socket.ChangeSink(this); // need to assign a new token id. token.TokenId = GetNewTokenId(); // put channel back in open state. ActivateToken(token); State = TcpChannelState.Open; // no need to cleanup. if (m_cleanupTimer != null) { m_cleanupTimer.Dispose(); m_cleanupTimer = null; } // send response. SendOpenSecureChannelResponse(requestId, token, request); // send any queue responses. ThreadPool.QueueUserWorkItem(new WaitCallback(OnChannelReconnected), m_queuedResponses); m_queuedResponses = new SortedDictionary<uint,IServiceResponse>(); } catch (Exception e) { SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); } } }
/// <summary> /// Handles a reconnect request. /// </summary> public virtual void Reconnect(IMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { throw new NotImplementedException(); }
/// <summary> /// Processes an OpenSecureChannel request message. /// </summary> private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk) { // validate the channel state. if (State != TcpChannelState.Opening && State != TcpChannelState.Open) { ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected OpenSecureChannel message."); return(false); } // parse the security header. uint channelId = 0; X509Certificate2 clientCertificate = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadAsymmetricMessage( messageChunk, ServerCertificate, out channelId, out clientCertificate, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessOpenSecureChannelRequest")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } } catch (Exception e) { ServiceResultException innerException = e.InnerException as ServiceResultException; // If the certificate structre, signature and trust list checks pass, we return the other specific validation errors instead of BadSecurityChecksFailed if (innerException != null) { if (innerException.StatusCode == StatusCodes.BadCertificateUntrusted || innerException.StatusCode == StatusCodes.BadCertificateChainIncomplete || innerException.StatusCode == StatusCodes.BadCertificateRevoked || innerException.StatusCode == StatusCodes.BadCertificateInvalid || (innerException.InnerResult != null && innerException.InnerResult.StatusCode == StatusCodes.BadCertificateUntrusted)) { ForceChannelFault(StatusCodes.BadSecurityChecksFailed, e.Message); return(false); } else if (innerException.StatusCode == StatusCodes.BadCertificateTimeInvalid || innerException.StatusCode == StatusCodes.BadCertificateIssuerTimeInvalid || innerException.StatusCode == StatusCodes.BadCertificateHostNameInvalid || innerException.StatusCode == StatusCodes.BadCertificateUriInvalid || innerException.StatusCode == StatusCodes.BadCertificateUseNotAllowed || innerException.StatusCode == StatusCodes.BadCertificateIssuerUseNotAllowed || innerException.StatusCode == StatusCodes.BadCertificateRevocationUnknown || innerException.StatusCode == StatusCodes.BadCertificateIssuerRevocationUnknown || innerException.StatusCode == StatusCodes.BadCertificateIssuerRevoked) { ForceChannelFault(innerException, innerException.StatusCode, e.Message); return(false); } } ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed, "Could not verify security on OpenSecureChannel request."); return(false); } BufferCollection chunksToProcess = null; try { bool firstCall = ClientCertificate == null; // must ensure the same certificate was used. if (ClientCertificate != null) { CompareCertificates(ClientCertificate, clientCertificate, false); } else { ClientCertificate = clientCertificate; } // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(false); } // create a new token. ChannelToken token = CreateToken(); token.TokenId = GetNewTokenId(); token.ServerNonce = CreateNonce(); // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); OpenSecureChannelRequest request = (OpenSecureChannelRequest)BinaryDecoder.DecodeMessage( new ArraySegmentStream(chunksToProcess), typeof(OpenSecureChannelRequest), Quotas.MessageContext); if (request == null) { throw ServiceResultException.Create(StatusCodes.BadStructureMissing, "Could not parse OpenSecureChannel request body."); } // check the security mode. if (request.SecurityMode != SecurityMode) { ReviseSecurityMode(firstCall, request.SecurityMode); } // check the client nonce. token.ClientNonce = request.ClientNonce; if (!ValidateNonce(token.ClientNonce)) { throw ServiceResultException.Create(StatusCodes.BadNonceInvalid, "Client nonce is not the correct length or not random enough."); } // choose the lifetime. int lifetime = (int)request.RequestedLifetime; if (lifetime < TcpMessageLimits.MinSecurityTokenLifeTime) { lifetime = TcpMessageLimits.MinSecurityTokenLifeTime; } if (lifetime > 0 && lifetime < token.Lifetime) { token.Lifetime = lifetime; } // check the request type. SecurityTokenRequestType requestType = request.RequestType; if (requestType == SecurityTokenRequestType.Issue && State != TcpChannelState.Opening) { throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request a new token for an open channel."); } if (requestType == SecurityTokenRequestType.Renew && State != TcpChannelState.Open) { // may be reconnecting to a dropped channel. if (State == TcpChannelState.Opening) { // tell the listener to find the channel that can process the request. Listener.ReconnectToExistingChannel( Socket, requestId, sequenceNumber, channelId, ClientCertificate, token, request); Utils.Trace( "{0} ReconnectToExistingChannel Socket={0:X8}, ChannelId={1}, TokenId={2}", ChannelName, (Socket != null) ? Socket.Handle : 0, (CurrentToken != null) ? CurrentToken.ChannelId : 0, (CurrentToken != null) ? CurrentToken.TokenId : 0); // close the channel. ChannelClosed(); // nothing more to do. return(false); } throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request to renew a token for a channel that has not been opened."); } // check the channel id. if (requestType == SecurityTokenRequestType.Renew && channelId != ChannelId) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Do not recognize the secure channel id provided."); } // log security information. if (requestType == SecurityTokenRequestType.Issue) { Opc.Ua.Security.Audit.SecureChannelCreated( m_ImplementationString, Listener.EndpointUrl.ToString(), Utils.Format("{0}", ChannelId), EndpointDescription, ClientCertificate, ServerCertificate, BinaryEncodingSupport.Required); } else { Opc.Ua.Security.Audit.SecureChannelRenewed( m_ImplementationString, Utils.Format("{0}", ChannelId)); } if (requestType == SecurityTokenRequestType.Renew) { SetRenewedToken(token); } else { ActivateToken(token); } State = TcpChannelState.Open; // send the response. SendOpenSecureChannelResponse(requestId, token, request); // notify reverse CompleteReverseHello(null); // notify any monitors. NotifyMonitors(ServiceResult.Good, false); return(false); } catch (Exception e) { SendServiceFault(requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing OpenSecureChannel request.")); CompleteReverseHello(e); return(false); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessOpenSecureChannelRequest"); } } }
/// <summary> /// Sends an OpenSecureChannel request. /// </summary> private void SendOpenSecureChannelRequest(bool renew) { // create a new token. TcpChannelToken token = CreateToken(); token.ClientNonce = CreateNonce(); // construct the request. OpenSecureChannelRequest request = new OpenSecureChannelRequest(); request.RequestHeader.Timestamp = DateTime.UtcNow; request.RequestType = (renew)?SecurityTokenRequestType.Renew:SecurityTokenRequestType.Issue; request.SecurityMode = SecurityMode; request.ClientNonce = token.ClientNonce; request.RequestedLifetime = (uint)Quotas.SecurityTokenLifetime; // encode the request. byte[] buffer = BinaryEncoder.EncodeMessage(request, Quotas.MessageContext); // write the asymmetric message. BufferCollection chunksToSend = WriteAsymmetricMessage( TcpMessageType.Open, m_handshakeOperation.RequestId, //ClientCertificateChain, ClientCertificate, ServerCertificate, new ArraySegment<byte>(buffer, 0, buffer.Length)); // save token. m_requestedToken = token; // write the message to the server. try { BeginWriteMessage(chunksToSend, Int32.MaxValue, m_handshakeOperation); chunksToSend = null; } finally { if (chunksToSend != null) { chunksToSend.Release(BufferManager, "SendOpenSecureChannelRequest"); } } }
private async Task OnRenewAsync(CancellationToken token) { var openSecureChannelRequest = new OpenSecureChannelRequest { ClientProtocolVersion = ProtocolVersion, RequestType = SecurityTokenRequestType.Renew, SecurityMode = this.RemoteEndpoint.SecurityMode, ClientNonce = this.symIsSigned ? this.GetNextNonce() : null, RequestedLifetime = TokenRequestedLifetime }; var openSecureChannelResponse = (OpenSecureChannelResponse)await this.RequestAsync(openSecureChannelRequest).ConfigureAwait(false); if (openSecureChannelResponse.ServerProtocolVersion < ProtocolVersion) { throw new ServiceResultException(StatusCodes.BadProtocolVersionUnsupported); } await this.sendingSemaphore.WaitAsync(token).ConfigureAwait(false); try { this.ChannelId = openSecureChannelResponse.SecurityToken.ChannelId; this.TokenId = openSecureChannelResponse.SecurityToken.TokenId; this.tokenRenewalTime = DateTime.UtcNow.AddMilliseconds(0.75 * openSecureChannelResponse.SecurityToken.RevisedLifetime); if (this.symIsSigned) { var clientNonce = openSecureChannelRequest.ClientNonce; var serverNonce = openSecureChannelResponse.ServerNonce; // (re)create client security keys for encrypting the next message sent var clientSecurityKey = CalculatePSHA(serverNonce, clientNonce, this.symSignatureKeySize + this.symEncryptionKeySize + this.symEncryptionBlockSize, this.asymSignatureHashAlgorithmName); Buffer.BlockCopy(clientSecurityKey, 0, this.clientSigningKey, 0, this.symSignatureKeySize); Buffer.BlockCopy(clientSecurityKey, this.symSignatureKeySize, this.clientEncryptingKey, 0, this.symEncryptionKeySize); Buffer.BlockCopy(clientSecurityKey, this.symSignatureKeySize + this.symEncryptionKeySize, this.clientInitializationVector, 0, this.symEncryptionBlockSize); // (re)create server security keys for decrypting the next message received that has a new TokenId var serverSecurityKey = CalculatePSHA(clientNonce, serverNonce, this.symSignatureKeySize + this.symEncryptionKeySize + this.symEncryptionBlockSize, this.asymSignatureHashAlgorithmName); Buffer.BlockCopy(serverSecurityKey, 0, this.serverSigningKey, 0, this.symSignatureKeySize); Buffer.BlockCopy(serverSecurityKey, this.symSignatureKeySize, this.serverEncryptingKey, 0, this.symEncryptionKeySize); Buffer.BlockCopy(serverSecurityKey, this.symSignatureKeySize + this.symEncryptionKeySize, this.serverInitializationVector, 0, this.symEncryptionBlockSize); } } finally { this.sendingSemaphore.Release(); } }
/// <summary> /// Binds a new socket to an existing channel. /// </summary> internal bool ReconnectToExistingChannel( TcpMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, TcpChannelToken token, OpenSecureChannelRequest request) { TcpServerChannel channel = null; lock (m_lock) { if (!m_channels.TryGetValue(channelId, out channel)) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Could not find secure channel referenced in the OpenSecureChannel request."); } } channel.Reconnect(socket, requestId, sequenceNumber, clientCertificate, token, request); // Utils.Trace("Channel {0} reconnected", channelId); return true; }
/// <summary> /// Initializes the message with the body. /// </summary> public OpenSecureChannelMessage(OpenSecureChannelRequest OpenSecureChannelRequest) { this.OpenSecureChannelRequest = OpenSecureChannelRequest; }
bool ITcpChannelListener.ReconnectToExistingChannel(IMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { throw new NotImplementedException(); }
protected override async Task OnOpenAsync(CancellationToken token) { await base.OnOpenAsync(token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); this.sendBuffer = new byte[this.LocalSendBufferSize]; this.receiveBuffer = new byte[this.LocalReceiveBufferSize]; if (this.LocalCertificate != null) { this.localCertificateBlob = this.LocalCertificate.RawData; } this.remoteCertificateBlob = this.RemoteEndpoint.ServerCertificate; if (this.remoteCertificateBlob != null) { this.RemoteCertificate = new X509Certificate2(this.remoteCertificateBlob); } if (this.RemoteEndpoint.SecurityMode == MessageSecurityMode.SignAndEncrypt) { if (this.LocalCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed, "LocalCertificate is null."); } if (this.RemoteCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed, "RemoteCertificate is null."); } this.LocalPrivateKey = this.LocalCertificate.GetRSAPrivateKey(); this.RemotePublicKey = this.RemoteCertificate.GetRSAPublicKey(); switch (this.RemoteEndpoint.SecurityPolicyUri) { case SecurityPolicyUris.Basic128Rsa15: this.asymEncryptionPadding = RSAEncryptionPadding.Pkcs1; this.asymSignatureHashAlgorithmName = HashAlgorithmName.SHA1; this.symSigner = new HMACSHA1(); this.symVerifier = new HMACSHA1(); this.symEncryptionAlgorithm = Aes.Create(); this.symEncryptionAlgorithm.Mode = CipherMode.CBC; this.symEncryptionAlgorithm.Padding = PaddingMode.None; this.asymLocalKeySize = this.LocalPrivateKey.KeySize; this.asymRemoteKeySize = this.RemotePublicKey.KeySize; this.asymLocalPlainTextBlockSize = Math.Max((this.asymLocalKeySize / 8) - 11, 1); this.asymRemotePlainTextBlockSize = Math.Max((this.asymRemoteKeySize / 8) - 11, 1); this.symSignatureSize = 20; this.symSignatureKeySize = 16; this.symEncryptionBlockSize = 16; this.symEncryptionKeySize = 16; break; case SecurityPolicyUris.Basic256: this.asymEncryptionPadding = RSAEncryptionPadding.OaepSHA1; this.asymSignatureHashAlgorithmName = HashAlgorithmName.SHA1; this.symSigner = new HMACSHA1(); this.symVerifier = new HMACSHA1(); this.symEncryptionAlgorithm = Aes.Create(); this.symEncryptionAlgorithm.Mode = CipherMode.CBC; this.symEncryptionAlgorithm.Padding = PaddingMode.None; this.asymLocalKeySize = this.LocalPrivateKey.KeySize; this.asymRemoteKeySize = this.RemotePublicKey.KeySize; this.asymLocalPlainTextBlockSize = Math.Max((this.asymLocalKeySize / 8) - 42, 1); this.asymRemotePlainTextBlockSize = Math.Max((this.asymRemoteKeySize / 8) - 42, 1); this.symSignatureSize = 20; this.symSignatureKeySize = 24; this.symEncryptionBlockSize = 16; this.symEncryptionKeySize = 32; break; case SecurityPolicyUris.Basic256Sha256: this.asymEncryptionPadding = RSAEncryptionPadding.OaepSHA1; this.asymSignatureHashAlgorithmName = HashAlgorithmName.SHA256; this.symSigner = new HMACSHA256(); this.symVerifier = new HMACSHA256(); this.symEncryptionAlgorithm = Aes.Create(); this.symEncryptionAlgorithm.Mode = CipherMode.CBC; this.symEncryptionAlgorithm.Padding = PaddingMode.None; this.asymLocalKeySize = this.LocalPrivateKey.KeySize; this.asymRemoteKeySize = this.RemotePublicKey.KeySize; this.asymLocalPlainTextBlockSize = Math.Max((this.asymLocalKeySize / 8) - 42, 1); this.asymRemotePlainTextBlockSize = Math.Max((this.asymRemoteKeySize / 8) - 42, 1); this.symSignatureSize = 32; this.symSignatureKeySize = 32; this.symEncryptionBlockSize = 16; this.symEncryptionKeySize = 32; break; default: throw new ServiceResultException(StatusCodes.BadSecurityPolicyRejected); } this.asymIsSigned = this.asymIsEncrypted = true; this.symIsSigned = true; this.symIsEncrypted = true; this.asymLocalSignatureSize = this.asymLocalKeySize / 8; this.asymLocalCipherTextBlockSize = Math.Max(this.asymLocalKeySize / 8, 1); this.asymRemoteSignatureSize = this.asymRemoteKeySize / 8; this.asymRemoteCipherTextBlockSize = Math.Max(this.asymRemoteKeySize / 8, 1); this.clientSigningKey = new byte[this.symSignatureKeySize]; this.clientEncryptingKey = new byte[this.symEncryptionKeySize]; this.clientInitializationVector = new byte[this.symEncryptionBlockSize]; this.serverSigningKey = new byte[this.symSignatureKeySize]; this.serverEncryptingKey = new byte[this.symEncryptionKeySize]; this.serverInitializationVector = new byte[this.symEncryptionBlockSize]; this.encryptionBuffer = new byte[this.LocalSendBufferSize]; } else if (this.RemoteEndpoint.SecurityMode == MessageSecurityMode.Sign) { if (this.LocalCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed, "LocalCertificate is null."); } if (this.RemoteCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed, "RemoteCertificate is null."); } this.LocalPrivateKey = this.LocalCertificate.GetRSAPrivateKey(); this.RemotePublicKey = this.RemoteCertificate.GetRSAPublicKey(); switch (this.RemoteEndpoint.SecurityPolicyUri) { case SecurityPolicyUris.Basic128Rsa15: this.asymEncryptionPadding = RSAEncryptionPadding.Pkcs1; this.asymSignatureHashAlgorithmName = HashAlgorithmName.SHA1; this.symSigner = new HMACSHA1(); this.symVerifier = new HMACSHA1(); this.asymLocalKeySize = this.LocalPrivateKey.KeySize; this.asymRemoteKeySize = this.RemotePublicKey.KeySize; this.asymLocalPlainTextBlockSize = Math.Max((this.asymLocalKeySize / 8) - 11, 1); this.asymRemotePlainTextBlockSize = Math.Max((this.asymRemoteKeySize / 8) - 11, 1); this.symSignatureSize = 20; this.symSignatureKeySize = 16; this.symEncryptionBlockSize = 16; this.symEncryptionKeySize = 16; break; case SecurityPolicyUris.Basic256: this.asymEncryptionPadding = RSAEncryptionPadding.OaepSHA1; this.asymSignatureHashAlgorithmName = HashAlgorithmName.SHA1; this.symSigner = new HMACSHA1(); this.symVerifier = new HMACSHA1(); this.asymLocalKeySize = this.LocalPrivateKey.KeySize; this.asymRemoteKeySize = this.RemotePublicKey.KeySize; this.asymLocalPlainTextBlockSize = Math.Max((this.asymLocalKeySize / 8) - 42, 1); this.asymRemotePlainTextBlockSize = Math.Max((this.asymRemoteKeySize / 8) - 42, 1); this.symSignatureSize = 20; this.symSignatureKeySize = 24; this.symEncryptionBlockSize = 16; this.symEncryptionKeySize = 32; break; case SecurityPolicyUris.Basic256Sha256: this.asymEncryptionPadding = RSAEncryptionPadding.OaepSHA1; this.asymSignatureHashAlgorithmName = HashAlgorithmName.SHA256; this.symSigner = new HMACSHA256(); this.symVerifier = new HMACSHA256(); this.asymLocalKeySize = this.LocalPrivateKey.KeySize; this.asymRemoteKeySize = this.RemotePublicKey.KeySize; this.asymLocalPlainTextBlockSize = Math.Max((this.asymLocalKeySize / 8) - 42, 1); this.asymRemotePlainTextBlockSize = Math.Max((this.asymRemoteKeySize / 8) - 42, 1); this.symSignatureSize = 32; this.symSignatureKeySize = 32; this.symEncryptionBlockSize = 16; this.symEncryptionKeySize = 32; break; default: throw new ServiceResultException(StatusCodes.BadSecurityPolicyRejected); } this.asymIsSigned = this.asymIsEncrypted = true; this.symIsSigned = true; this.symIsEncrypted = false; this.asymLocalSignatureSize = this.asymLocalKeySize / 8; this.asymLocalCipherTextBlockSize = Math.Max(this.asymLocalKeySize / 8, 1); this.asymRemoteSignatureSize = this.asymRemoteKeySize / 8; this.asymRemoteCipherTextBlockSize = Math.Max(this.asymRemoteKeySize / 8, 1); this.clientSigningKey = new byte[this.symSignatureKeySize]; this.clientEncryptingKey = new byte[this.symEncryptionKeySize]; this.clientInitializationVector = new byte[this.symEncryptionBlockSize]; this.serverSigningKey = new byte[this.symSignatureKeySize]; this.serverEncryptingKey = new byte[this.symEncryptionKeySize]; this.serverInitializationVector = new byte[this.symEncryptionBlockSize]; this.encryptionBuffer = new byte[this.LocalSendBufferSize]; } else if (this.RemoteEndpoint.SecurityMode == MessageSecurityMode.None) { this.asymIsSigned = this.asymIsEncrypted = false; this.symIsSigned = this.symIsEncrypted = false; this.asymLocalKeySize = 0; this.asymRemoteKeySize = 0; this.asymLocalSignatureSize = 0; this.asymLocalCipherTextBlockSize = 1; this.asymRemoteSignatureSize = 0; this.asymRemoteCipherTextBlockSize = 1; this.asymLocalPlainTextBlockSize = 1; this.asymRemotePlainTextBlockSize = 1; this.symSignatureSize = 0; this.symSignatureKeySize = 0; this.symEncryptionBlockSize = 1; this.symEncryptionKeySize = 0; this.encryptionBuffer = null; } else { throw new ServiceResultException(StatusCodes.BadSecurityModeRejected); } this.sendRequestsTask = Task.Run(() => this.SendRequestsAsync(this.channelCts.Token)); this.receiveResponsesTask = Task.Run(() => this.ReceiveResponsesAsync(this.channelCts.Token)); var openSecureChannelRequest = new OpenSecureChannelRequest { ClientProtocolVersion = ProtocolVersion, RequestType = SecurityTokenRequestType.Issue, SecurityMode = this.RemoteEndpoint.SecurityMode, ClientNonce = this.symIsSigned ? this.GetNextNonce() : null, RequestedLifetime = TokenRequestedLifetime }; var openSecureChannelResponse = (OpenSecureChannelResponse)await this.RequestAsync(openSecureChannelRequest).ConfigureAwait(false); if (openSecureChannelResponse.ServerProtocolVersion < ProtocolVersion) { throw new ServiceResultException(StatusCodes.BadProtocolVersionUnsupported); } await this.sendingSemaphore.WaitAsync(token).ConfigureAwait(false); try { this.ChannelId = openSecureChannelResponse.SecurityToken.ChannelId; this.TokenId = openSecureChannelResponse.SecurityToken.TokenId; this.tokenRenewalTime = DateTime.UtcNow.AddMilliseconds(0.75 * openSecureChannelResponse.SecurityToken.RevisedLifetime); if (this.symIsSigned) { var clientNonce = openSecureChannelRequest.ClientNonce; var serverNonce = openSecureChannelResponse.ServerNonce; // (re)create client security keys for encrypting the next message sent var clientSecurityKey = CalculatePSHA(serverNonce, clientNonce, this.symSignatureKeySize + this.symEncryptionKeySize + this.symEncryptionBlockSize, this.asymSignatureHashAlgorithmName); Buffer.BlockCopy(clientSecurityKey, 0, this.clientSigningKey, 0, this.symSignatureKeySize); Buffer.BlockCopy(clientSecurityKey, this.symSignatureKeySize, this.clientEncryptingKey, 0, this.symEncryptionKeySize); Buffer.BlockCopy(clientSecurityKey, this.symSignatureKeySize + this.symEncryptionKeySize, this.clientInitializationVector, 0, this.symEncryptionBlockSize); // (re)create server security keys for decrypting the next message received that has a new TokenId var serverSecurityKey = CalculatePSHA(clientNonce, serverNonce, this.symSignatureKeySize + this.symEncryptionKeySize + this.symEncryptionBlockSize, this.asymSignatureHashAlgorithmName); Buffer.BlockCopy(serverSecurityKey, 0, this.serverSigningKey, 0, this.symSignatureKeySize); Buffer.BlockCopy(serverSecurityKey, this.symSignatureKeySize, this.serverEncryptingKey, 0, this.symEncryptionKeySize); Buffer.BlockCopy(serverSecurityKey, this.symSignatureKeySize + this.symEncryptionKeySize, this.serverInitializationVector, 0, this.symEncryptionBlockSize); } } finally { this.sendingSemaphore.Release(); } }
private async Task SendOpenSecureChannelRequestAsync(OpenSecureChannelRequest request, CancellationToken token) { var bodyStream = SerializableBytes.CreateWritableStream(); var bodyEncoder = new BinaryEncoder(bodyStream, this); try { bodyEncoder.WriteNodeId(null, OpenSecureChannelRequestNodeId); request.Encode(bodyEncoder); bodyStream.Position = 0; if (bodyStream.Length > this.RemoteMaxMessageSize) { throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded); } // write chunks int chunkCount = 0; int bodyCount = (int)(bodyStream.Length - bodyStream.Position); while (bodyCount > 0) { chunkCount++; if (this.RemoteMaxChunkCount > 0 && chunkCount > this.RemoteMaxChunkCount) { throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded); } var stream = new MemoryStream(this.sendBuffer, 0, (int)this.RemoteReceiveBufferSize, true, true); var encoder = new BinaryEncoder(stream, this); try { // header encoder.WriteUInt32(null, UaTcpMessageTypes.OPNF); encoder.WriteUInt32(null, 0u); encoder.WriteUInt32(null, this.ChannelId); // asymmetric security header encoder.WriteString(null, this.RemoteEndpoint.SecurityPolicyUri); if (this.RemoteEndpoint.SecurityMode != MessageSecurityMode.None) { encoder.WriteByteString(null, this.localCertificateBlob); encoder.WriteByteString(null, this.RemoteCertificate.GetCertHash()); } else { encoder.WriteByteString(null, null); encoder.WriteByteString(null, null); } int plainHeaderSize = encoder.Position; // sequence header encoder.WriteUInt32(null, this.GetNextSequenceNumber()); encoder.WriteUInt32(null, request.RequestHeader.RequestHandle); // body int paddingHeaderSize; int maxBodySize; int bodySize; int paddingSize; int chunkSize; if (this.asymIsEncrypted) { paddingHeaderSize = this.asymRemoteCipherTextBlockSize > 256 ? 2 : 1; maxBodySize = ((((int)this.RemoteReceiveBufferSize - plainHeaderSize - this.asymLocalSignatureSize - paddingHeaderSize) / this.asymRemoteCipherTextBlockSize) * this.asymRemotePlainTextBlockSize) - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; paddingSize = (this.asymRemotePlainTextBlockSize - ((SequenceHeaderSize + bodySize + paddingHeaderSize + this.asymLocalSignatureSize) % this.asymRemotePlainTextBlockSize)) % this.asymRemotePlainTextBlockSize; } else { bodySize = maxBodySize; paddingSize = 0; } chunkSize = plainHeaderSize + (((SequenceHeaderSize + bodySize + paddingSize + paddingHeaderSize + this.asymLocalSignatureSize) / this.asymRemotePlainTextBlockSize) * this.asymRemoteCipherTextBlockSize); } else { paddingHeaderSize = 0; paddingSize = 0; maxBodySize = (int)this.RemoteReceiveBufferSize - plainHeaderSize - this.asymLocalSignatureSize - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; } else { bodySize = maxBodySize; } chunkSize = plainHeaderSize + SequenceHeaderSize + bodySize + this.asymLocalSignatureSize; } bodyStream.Read(this.sendBuffer, encoder.Position, bodySize); encoder.Position += bodySize; bodyCount -= bodySize; // padding if (this.asymIsEncrypted) { byte paddingByte = (byte)(paddingSize & 0xFF); encoder.WriteByte(null, paddingByte); for (int i = 0; i < paddingSize; i++) { encoder.WriteByte(null, paddingByte); } if (paddingHeaderSize == 2) { byte extraPaddingByte = (byte)((paddingSize >> 8) & 0xFF); encoder.WriteByte(null, extraPaddingByte); } } // update message type and (encrypted) length var position = encoder.Position; encoder.Position = 3; encoder.WriteByte(null, bodyCount > 0 ? (byte)'C' : (byte)'F'); encoder.WriteUInt32(null, (uint)chunkSize); encoder.Position = position; // sign if (this.asymIsSigned) { // sign with local private key. byte[] signature = this.LocalPrivateKey.SignData(this.sendBuffer, 0, position, this.asymSignatureHashAlgorithmName, RSASignaturePadding.Pkcs1); Debug.Assert(signature.Length == this.asymLocalSignatureSize, nameof(this.asymLocalSignatureSize)); encoder.Write(signature, 0, this.asymLocalSignatureSize); } // encrypt if (this.asymIsEncrypted) { position = encoder.Position; Buffer.BlockCopy(this.sendBuffer, 0, this.encryptionBuffer, 0, plainHeaderSize); byte[] plainText = new byte[this.asymRemotePlainTextBlockSize]; int jj = plainHeaderSize; for (int ii = plainHeaderSize; ii < position; ii += this.asymRemotePlainTextBlockSize) { Buffer.BlockCopy(this.sendBuffer, ii, plainText, 0, this.asymRemotePlainTextBlockSize); // encrypt with remote public key. byte[] cipherText = this.RemotePublicKey.Encrypt(plainText, this.asymEncryptionPadding); Debug.Assert(cipherText.Length == this.asymRemoteCipherTextBlockSize, nameof(this.asymRemoteCipherTextBlockSize)); Buffer.BlockCopy(cipherText, 0, this.encryptionBuffer, jj, this.asymRemoteCipherTextBlockSize); jj += this.asymRemoteCipherTextBlockSize; } await this.SendAsync(this.encryptionBuffer, 0, jj, token).ConfigureAwait(false); return; } // pass buffer to transport await this.SendAsync(this.sendBuffer, 0, encoder.Position, token).ConfigureAwait(false); } finally { encoder.Dispose(); } } } finally { bodyEncoder.Dispose(); } }