Glossary Item Box
PowerTCP goals in providing secure communications over a network.
Historically, the PowerTCP secure implementation has been to combine security and protocol in a single product. This resulted in products like the ActiveX SecureTcp control, which was designed to communicate securely over a network. However, we found this design, while certainly functional, was limiting and inefficient. The major drawbacks to this implementation are:
For these reasons, we decided to separate the security implementation from the protocol implementation. This resulted in the creation of a Tcp component, whose job it is to communicate over a network, and an SslStream, whose job it is to encrypt/decrypt data and perform SSL authentication. The Tcp component can be used on it's own to send normal, "plain-text" data, or in conjunction with the SslStream to send encrypted data. This design removed many inefficiencies and duplicated functionality, as the same SslStream can be used with other components to provide SSL support to any protocol.
How is this achieved? In order for the SslStream to be able to "plug-in" to any secure implementation, we had to be sure all entities use a common interface. This was achieved through the creation of a PipeStream object.
What is a PipeStream?
A PipeStream is a stream class which "contains" another stream. When you read or write to the PipeStream, the data is actually read from/written to the internal, "contained" stream. Essentially, the stream acts as a "pipe", simply passing data through to the next stream.
It may help to conceptualize this by just thinking about two entities, the aforementioned "PipeStream" and a theoretical "EndpointStream". The PipeStream acts as described above. It contains an "internal" stream to/from which all reads/writes are made. This "internal" stream is either another PipeStream, or an "EndPointStream". The "EndpointStream" is the stream at the end of the "pipe". This can be thought of as a "destination" stream for written data and a "source" stream for read data. For example, the "EndPointStream" could be a FileStream. This means all data that is read comes from a file, passes through the PipeStream, and is received into a buffer in your application. This also means that all data that is written comes from your application, passes through the PipeStream, and is written to the file. Keep in mind in this example this means that the PipeStream's "internal" stream is the FileStream. Also, the "internal" stream of the PipeStream is exposed in the PipeStream.CoreStream property.
So, essentially all data that comes from the FileStream, and all data written to the FileStream, first has to pass through a PipeStream. This behavior, on it's own, certainly is not very useful. However, the intended use for a PipeStream is to create a new class, inheriting from PipeStream, which modifies (encrypts/decrypts/filters) the data passing through it. Let's extend the above example to make it a bit more useful. First of all, assume your FileStream is always writing to/reading from a text file. Then, assume you wish for all data to be written to the file in Spanish, so you can share it with your overseas reseller. Of course when reading from this file, it has to be converted back to English so you can use it within your application. To do this, simply create a new class, inheriting from PipeStream, which translates outgoing data to Spanish and incoming data to English.
PipeStreams Explained: A Language Translation Example
The first question one might ask at this point is "Couldn't I just have created a new stream, inheriting from System.IO.FileStream, which does the translation?". Yes, this is true. But the PipeStream paradigm is much more flexible than this. For example, say now all of the sudden your overseas reseller requires their text file to be an XML formatted file. No problem, instead of having to rewrite your English-Spanish stream to account for this, just create a new PipeStream that formats outgoing data into XML, and parses incoming data into plain text.
So, in this example. There is an EnglishSpanishStream, whose internal stream is a XmlFormattingStream. The XmlFormattingStream's internal stream is a System.IO.FileStream. Now, when EnglishSpanishStream.Write is called, the data is first translated to Spanish, formatted in XML, then written to a text file. When EnglishSpanishStream.Read is called, the data is first parsed to have all XML formatting removed, translated to English, then returned to your buffer in your application.
So now the flexibility may be a little more apparent. Here are some more examples which may help to illustrate.
You wish to provide the service explained above to a new French reseller. Create a new EnglishFrenchStream to replace the EnglishSpanishStream.
The Spanish reseller no longer wants XML formatting. Remove the XmlFormattingStream.
What would the code look like for this? Well, obviously the code is going to be quite complex for a language translator, and fairly complex for an XML formatter, so we will assume you have already created these streams. However, we can demonstrate how you would initialize these streams to perform the action described above. You set a PipeStream's "internal" stream by passing it into the constructor. The English-French example would look something like:
// Create the PipeStream "combination" EnglishFrenchStream engfr = new EnglishFrenchStream(new XmlFormattingStream(new System.IO.FileStream("C:\\myfile", System.IO.FileMode.Create))); // Now call EnglishFrenchStream.Read or .Write
Back to network communication.
The above explanation hopefully serves to provide the background needed to understand why and how PipeStreams are used. Now to switch from our theoretical example to an actual implementation examine our Tcp component. Normal communication involving the Tcp component involves two entities:
When you combine both of these entities, it is clear that this implementation is used to read from and write to network sockets using enhanced reading and writing capabilities. Now take a look at secure communication, which involves three entities.
The following diagram further illustrates the implementation described above.
You can easily switch the behavior of the Tcp component by changing which PipeStream "combination" is used to initialize the Stream property.
[C#] // Initialize the Stream property to a new PipeStream "combination", enabling SSL communication. tcp1.Stream = new SegmentedStream(new SslStream(new TcpStream(tcp1))); // Switch back to a non-secure PipeStream "combination. tcp1.Stream = new SegmentedStream(new TcpStream(tcp1)); [Visual Basic] ' Initialize the Stream property to a new PipeStream "combination", enabling SSL communication. Tcp1.Stream = new SegmentedStream(new SslStream(new TcpStream(tcp1))) ' Switch back to a non-secure PipeStream "combination. Tcp1.Stream = new SegmentedStream(new TcpStream(tcp1))
Other PipeStream uses.
It is often useful to use PipeStream types outside of network communication. A good example of this is using the SymmetricCryptoStream, an object we have included to make data encryption/decryption easier which provides the ability to encrypt/decrypt data using a key. Simply initialize a new SymmetricCryptoStream with a System.IO.FileStream as it's "EndPointStream" and when data is written to the SymmetricCryptoStream, it is encrypted and written to the file. When data is read from the SymmetricCryptoStream, it is decrypted.
Send comments on this topic.
Documentation version 1.1.2.0.
© 2008 Dart Communications. All rights reserved.