private async Task SendCloseSecureChannelRequestAsync(CloseSecureChannelRequest request, CancellationToken token) { var bodyStream = SerializableBytes.CreateWritableStream(); var bodyEncoder = new BinaryEncoder(bodyStream, this); try { bodyEncoder.WriteNodeId(null, CloseSecureChannelRequestNodeId); 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.CLOF); encoder.WriteUInt32(null, 0u); encoder.WriteUInt32(null, this.ChannelId); // symmetric security header encoder.WriteUInt32(null, this.TokenId); // detect new TokenId if (this.TokenId != this.currentClientTokenId) { this.currentClientTokenId = this.TokenId; // update signer and encrypter with new symmetric keys if (this.symIsSigned) { this.symSigner.Key = this.clientSigningKey; if (this.symIsEncrypted) { this.currentClientEncryptingKey = this.clientEncryptingKey; this.currentClientInitializationVector = this.clientInitializationVector; this.symEncryptor = this.symEncryptionAlgorithm.CreateEncryptor(this.currentClientEncryptingKey, this.currentClientInitializationVector); } } } 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.symIsEncrypted) { paddingHeaderSize = this.symEncryptionBlockSize > 256 ? 2 : 1; maxBodySize = ((((int)this.RemoteReceiveBufferSize - plainHeaderSize - this.symSignatureSize - paddingHeaderSize) / this.symEncryptionBlockSize) * this.symEncryptionBlockSize) - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; paddingSize = (this.symEncryptionBlockSize - ((SequenceHeaderSize + bodySize + paddingHeaderSize + this.symSignatureSize) % this.symEncryptionBlockSize)) % this.symEncryptionBlockSize; } else { bodySize = maxBodySize; paddingSize = 0; } chunkSize = plainHeaderSize + (((SequenceHeaderSize + bodySize + paddingSize + paddingHeaderSize + this.symSignatureSize) / this.symEncryptionBlockSize) * this.symEncryptionBlockSize); } else { paddingHeaderSize = 0; paddingSize = 0; maxBodySize = (int)this.RemoteReceiveBufferSize - plainHeaderSize - this.symSignatureSize - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; } else { bodySize = maxBodySize; } chunkSize = plainHeaderSize + SequenceHeaderSize + bodySize + this.symSignatureSize; } bodyStream.Read(this.sendBuffer, encoder.Position, bodySize); encoder.Position += bodySize; bodyCount -= bodySize; // padding if (this.symIsEncrypted) { var paddingByte = (byte)(paddingSize & 0xFF); encoder.WriteByte(null, paddingByte); for (int i = 0; i < paddingSize; i++) { encoder.WriteByte(null, paddingByte); } if (paddingHeaderSize == 2) { var 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; // signature if (this.symIsSigned) { byte[] signature = this.symSigner.ComputeHash(this.sendBuffer, 0, position); if (signature != null) { encoder.Write(signature, 0, signature.Length); } } // encrypt position = encoder.Position; if (this.symIsEncrypted) { using (var symEncryptor = this.symEncryptionAlgorithm.CreateEncryptor(this.currentClientEncryptingKey, this.currentClientInitializationVector)) { int inputCount = position - plainHeaderSize; Debug.Assert(inputCount % symEncryptor.InputBlockSize == 0, "Input data is not an even number of encryption blocks."); symEncryptor.TransformBlock(this.sendBuffer, plainHeaderSize, inputCount, this.sendBuffer, plainHeaderSize); symEncryptor.TransformFinalBlock(this.sendBuffer, position, 0); } } // pass buffer to transport await this.SendAsync(this.sendBuffer, 0, position, token).ConfigureAwait(false); } finally { encoder.Dispose(); } } } finally { bodyEncoder.Dispose(); } }
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(); } }