PeerChannel's security

How can I secure PeerChannel

(edited to reflect Feb CTP OM changes)

PeerChannel provides two modes of peer-to-peer authentication (to prevent un-authorized people from joining your mesh) and option to sign application messages (to prevent unauthorized sources from tempering with your message).

These security settings are specified using Security property on NetPeerTcpBinding. This property behaves the same way as many of the other indigo bindings. Security.Mode specifies what mode of security is required : SecurityMode.None means no security is required, SecurityMode.Transport means only neighbor-to-neighbor authentication is required (but no message security). SecurityMode.Message means message authentication is required over open channels, SecurityMode.TransportWithMessageCredentials implies that message authentication is required over secure neighbor-to-neighbor channels. Specifying either Message or TransportWithMessageCredential requires that all messages sent and received be secured using an X509Certificate2 credential.

Authentication

PeerChannel provides two kinds of neighbor-to-neighbor authentication. These choices are captured in Security.Transport.CredentialType property. A value of PeerTransportCredentialType.Password means that password based authentication requires a valid password be specified by each neighbor. if PeerTransportCredentialType.Certificate means that peer authentication is based on  X509Certificates. Both authentication modes supported by PeerChannel are mutual. To turn off authentication, you set binding.Security.Mode = SecurityMode.None . You should carefully consider ramifications of turning off authentication. Without authentication, anyone may join a mesh just by knowing the name of the mesh.

PeerTransportCredentialType.Password : in this mode, all peers wanting to participate in a mesh, must provide the same password. For example; if you are the owner of a mesh, you establish a mesh name, and communicate the name along with a (preferably strong) password to all your friends, whom you want to invite into the mesh.

The way the password mode works is very simple. Lets consider a simple walkthru of a peer joining a mesh. when the application opens a peerchannel instance, it starts up an instance of a peerchannel transport manager. the transport manager, contacts the resolver (either custom resolver or the default PNRP resolver) by passing the mesh name and requesting endpoint addresses of few neighbors. the transport manager then initiates connection requests to each of the peers.

In the password authentication mode, the same sequence of steps happens when a neighbor initiates a connection request except two key differences: 1) the link between the two neighbors is an SSL connection (meaning all traffic flowing between the neighbors is encrypted and signed). 2) Immediately after connecting, the initiator will send a custom handshake message that proves the knowledge of password (this is not the password, but a derivative of password and other key materials.). If the responder is satisfied with the data (which the responder can independently verify with the same password and key materials), the responder accepts the connection and sends back a similar message. If the initiator is satisfied with the response, he/she keeps the connection. Otherwise, bye bye.

To accomplish the above handshake, the PeerChannel must know two pieces of information: a certificate using which a secure neighbor-to-neighbor channel can be established. A password to do the above handshake. Both of these credentials can be specified using PeerCredential like this:

// Create a PeerSecurityBehavior instance to pass PeerChannel-specific credentials.
// for PeerAuthenticationMode.Password, you need to specify:
// 1) a certificate to establish secure neighbor-to-neighbor connection
// 2) a password that is specific to that mesh and known to all mesh participants.

//create a channel factory. Again, config-based approach is shown here.
using (ChannelFactory<IChatChannel> cf = new ChannelFactory<IChatChannel>("SecureChatEndpoint" ))
{

// Make sure you insist that the user provide 'strong' password.
cf.Credentials.Peer.MeshPassword = aStrongPassword;

// For brevity, details of how certificate is loaded are removed. It is application-specific activity
X509Certificate2 certificate = GetCertificate(...);

// set it on the PeerSecurityBehavior
cf.Credentials.Peer.Certificate = certificate;

   // now, create a proxy
   using (IChatChannel participant = cf.CreateDuplexChannel(myInstanceContext))
    {
       // Retrieve the PeerNode associated with the participant and register for online/offline events
       // PeerNode represents a node in the mesh. Mesh is the named collection of connected nodes.

      PeerNode node = (participant as IClientChannel).Extensions.Find<PeerNode >();
       node.Online += new EventHandler (OnOnline);
       node.Offline += new EventHandler (OnOffline);

      // open the channel
       participant.Open();

      // you are ready to exchange messages
    }
}

Only if you are creating a input-only channel (as in ServiceHost), you can do the same using serviceHost.Credentials.Peer.

Now lets talk about the other mode of authentication:

PeerTransportCredentialType.Certificate

   In this mode, the application has direct control of the authentication process. As in Password mode, a secure connection is established between neighbors. But rather than performing a custom handshake, the peerchannel implementation handsover the decision to the application passing along the certificate of the remote peer. Application says either "yes", in which case the connection is accepted  or "no", in which case, the connection is torn down.

To get this mode correctly working, the application has to specify two pieces of input to PeerChannel: 1) a certificate to establish SSL connection and also prove the identity of the peer. 2) an application-specific implementation of X509CertificateValidator. This type is an abstract class that has a single method: Validate(X509Certificate2). As you can guess, when a new neighbor connection is established, this method is called by WCF infrastructure, asking your application to make a decision. To reject a connection (if you dont trust the certificate for any reason), just throw SecurityTokenValidationException. that will close the neighbor connection.

Here is how you specify these credentials :

// Create a PeerSecurityBehavior instance to pass PeerChannel-specific credentials.
// for PeerAuthenticationMode.MutualCertificate, you need to specify:
// 1) a certificate to establish secure neighbor-to-neighbor connection
// 2) a custom X509CertificateValidator implementation that knows how to authenticate peers.

// specify your certificate for establishing an SSL connection
// for brevity, details of how certificate is loaded are removed. It is application-specific activity

X509Certificate2 certificate = GetCertificate(...);

// set it on the PeerSecurityBehavior

cf.Credentials.Peer.Certificate = certificate;

// specify an implementation of X509CertificateValidator

cf.Credentials.Peer.PeerAuthentication.CertificateValidationMode = X509CertificateValidationMode.Custom

cf.Credentials.Peer.PeerAuthentication.CustomCertificateValidator = myValidator;

// rest of it is the same. so removed from here. see further down for a complete example.

Ofcourse, the binding will have Security.Transport.CredentialType = PeerTransportCredentialType.Certificate set. if you are using config to do this, you can use the following attribute on netPeerTcpBinding

<security mode="Transport">

   <transport credentialType="Certificate" />

</security>

MessageSigning:

      For some application types, it is important to not only authenticate all neighbors, but also to ensure that application messages are not tampered in-flight. For example, consider a news broadcasting app or a content distribution service: you dont want someone tweaking your news, or deliver software patches with virus (BTW: I'm not suggesting a design here: just playing with an example).

To accomplish this, you can ask PeerChannel to include/verify the signature in each message. To create a signature, you can use a certificte (that includes private key). To verify the signature, you need the same certificate, but dont need the private key. These signatures are included in the application message in such a way that they can be verified by all peers in the mesh (as long as they have the public key). But how can PeerChannel verify signatures that are specific to your application? very simple : PeerChannel provides you with a hook where you can participate in the indigo's signature verification dance.

   Here is how you implement the receiver side of the application:

   first, remember to turn on message authentication on the binding.

You can do it either config way (by specifying security element's mode attribute  to "Message" or "TransportWithMessageCredential"on netPeerTcpBinding element) or, imperative way:

NetPeerTcpBinding binding =

new NetPeerTcpBinding();

binding.Security.Mode = SecurityMode.Message;

//create an instance of message validator. details of loading the certificate are omitted
GossipMessageValidator myMessageValidator = new GossipMessageValidator(...);

channelFactory.Credentials.Peer.MessageSenderAuthentication.CertificateValidationMode = X509CertificateValidationMode.Custom;

channelFactory.Credentials.Peer.MessageSenderAuthentication.CustomCertificateValidator = myMessageValidator;

But how do you implement this validator? what is the type? simple: its an application-specific implementation of the abstract type: X509CertificateValidator (remember from authentication discussion?). Whenever a new application message arrives on your channel, indigo infrastructure calls your validator passing the credentials embedded in the message. All you have to do is: tell indigo if you like the credentials in the message or not. You dont have to bother if the message is tampered, if all parts are signed etc etc. It is all taken care of for you.

 

For example: I'm writing a broadcast application: In this app (in this specific mesh instance say), I only allow messages originating from GossipsRus.com.

For simplicity, lets assume we turn off authentication.

Lets also assume that all peers trust this publisher GossipsRus.com (this means the certificate can be found in the per user store/machine store).

Following demonstrates how to validate messages received on a channel with message signing (interchangeably used with MessageAuthentication):

class GossipMessageValidator : X509CertificateValidator
{
    X509Certificate2 accept;

   public GossipMessageValidator( X509Certificate2 blab)
{
accept = blab;
   }

   public override void Validate( X509Certificate2 who)
{
       if (who == null || (0 != String .CompareOrdinal(who.Thumbprint, accept.Thumbprint)))
          throw new SecurityTokenValidationException( "Unrecognized sender" );
}
}

As you can see, you can use any cryterion to pass/fail validation: a list of trusted certificates, or certificates issued by a trusted publisher etc etc,...

You write almost similar code for writing sernder-side of this application. you provide the same validator as above. A sender needs one additional piece of information: he needs to secure each outgoing message with a credential (ok: X509 Certificate) that all receivers can verify the message with.

So you specify that credential using:

Credentials.Peer.Certificate

Yes. you are correct: this is the same certificate you provided to do authentication.

here is more lines of code demonstrating it:

NetPeerTcpBinding

binding = new NetPeerTcpBinding ();

binding.Security.Mode =

SecurityMode.Message;

// Create an instance of message validator. details of loading the certificate are omitted

GossipMessageValidator myMessageValidator = new GossipMessageValidator (...);

Credentials.Peer.MessageSenderAuthentication.CertificateValidationMode = X509CertificateValidationMode.Custom;

Credentials.Peer.MessageSenderAuthentication.CustomCertificateValidator = myMessageValidator;

X509Certificate2 publisherCredential = GetPublisherCredential(...);

//set the following for channels that you use to send messages on.
Credentials.Peer.Certificate = publisherCredential;

Have fun playing with PeerChannel.

- Ram Pamulapati


This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at: https://www.microsoft.com/info/cpyright.htm"