Skip to content
Permalink
Browse files
https://issues.apache.org/activemq/browse/AMQNET-239
Add an SslTransport to the Stomp Client and throw an exception when Ssl is specified on NETCF platforms.
  • Loading branch information
Timothy A. Bish committed Mar 9, 2010
1 parent 2b2bcfe commit d85134b6ba85d8bef4d796f18aaa3ebd2350c9e8
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 8 deletions.
@@ -0,0 +1,172 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#if !NETCF

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

namespace Apache.NMS.Stomp.Transport.Tcp
{
public class SslTransport : TcpTransport
{
private string clientCertLocation;
private string clientCertPassword;

private bool acceptInvalidBrokerCert = false;

private SslStream sslStream;

public SslTransport(Uri location, Socket socket, IWireFormat wireFormat) :
base(location, socket, wireFormat)
{
}

~SslTransport()
{
Dispose(false);
}

/// <summary>
/// Indicates the location of the Client Certificate to use when the Broker
/// is configured for Client Auth (not common). The SslTransport will supply
/// this certificate to the SslStream via the SelectLocalCertificate method.
/// </summary>
public string ClientCertLocation
{
get { return this.clientCertLocation; }
set { this.clientCertLocation = value; }
}

/// <summary>
/// Password for the Client Certificate specified via configuration.
/// </summary>
public string ClientCertPassword
{
get { return this.clientCertPassword; }
set { this.clientCertPassword = value; }
}

/// <summary>
/// Indicates if the SslTransport should ignore any errors in the supplied Broker
/// certificate and connect anyway, this is useful in testing with a default AMQ
/// broker certificate that is self signed.
/// </summary>
public bool AcceptInvalidBrokerCert
{
get { return this.acceptInvalidBrokerCert; }
set { this.acceptInvalidBrokerCert = value; }
}

protected override Stream CreateSocketStream()
{
if(this.sslStream != null)
{
return this.sslStream;
}

LocalCertificateSelectionCallback clientCertSelect = null;

if(this.clientCertLocation != null )
{
clientCertSelect = new LocalCertificateSelectionCallback(SelectLocalCertificate);
}

this.sslStream = new SslStream(
new NetworkStream(this.socket),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
clientCertSelect );

try
{
Tracer.Debug("Authorizing as Client for Server: " + this.RemoteAddress.Host);
sslStream.AuthenticateAsClient(this.RemoteAddress.Host);
Tracer.Debug("Server is Authenticated = " + sslStream.IsAuthenticated);
Tracer.Debug("Server is Encrypted = " + sslStream.IsEncrypted);
}
catch(Exception e)
{
Tracer.ErrorFormat("Exception: {0}", e.Message);
if(e.InnerException != null)
{
Tracer.ErrorFormat("Inner exception: {0}", e.InnerException.Message);
}
Tracer.Error("Authentication failed - closing the connection.");

throw e;
}

return sslStream;
}

private bool ValidateServerCertificate(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
Tracer.DebugFormat("ValidateServerCertificate: Issued By {0}", certificate.Issuer);
if(sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}

Tracer.WarnFormat("Certificate error: {0}", sslPolicyErrors.ToString());
if(sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
{
Tracer.Error("Chain Status errors: ");
foreach( X509ChainStatus status in chain.ChainStatus )
{
Tracer.Error("*** Chain Status error: " + status.Status);
Tracer.Error("*** Chain Status information: " + status.StatusInformation);
}
}
else if(sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch)
{
Tracer.Error("Mismatch between Remote Cert Name.");
}
else if(sslPolicyErrors == SslPolicyErrors.RemoteCertificateNotAvailable)
{
Tracer.Error("The Remote Certificate was not Available.");
}

// Configuration may or may not allow us to connect with an invliad broker cert.
return AcceptInvalidBrokerCert;
}

private X509Certificate SelectLocalCertificate(object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{
Tracer.Debug("Client is selecting a local certificate.");

X509Certificate2 certificate = new X509Certificate2( clientCertLocation, clientCertPassword );

return certificate;
}

}
}

#endif
@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Net;
using System.Net.Sockets;

namespace Apache.NMS.Stomp.Transport.Tcp
{
public class SslTransportFactory : TcpTransportFactory
{
private string clientCertLocation;
private string clientCertPassword;
private bool acceptInvalidBrokerCert = false;

public SslTransportFactory() : base()
{
}

public string ClientCertLocation
{
get { return this.clientCertLocation; }
set { this.clientCertLocation = value; }
}

public string ClientCertPassword
{
get { return this.clientCertPassword; }
set { this.clientCertPassword = value; }
}

public bool AcceptInvalidBrokerCert
{
get { return this.acceptInvalidBrokerCert; }
set { this.acceptInvalidBrokerCert = value; }
}

protected override ITransport DoCreateTransport(Uri location, Socket socket, IWireFormat wireFormat )
{
Tracer.Debug("Creating new instance of the SSL Transport.");
#if !NETCF
SslTransport transport = new SslTransport(location, socket, wireFormat);

transport.ClientCertLocation = ClientCertLocation;
transport.ClientCertPassword = ClientCertPassword;
transport.AcceptInvalidBrokerCert = AcceptInvalidBrokerCert;

return transport;
#else
throw new NotSupportedException("SslTransport not implemented on the .NET Compact Framework.");
#endif
}
}
}
@@ -29,8 +29,8 @@ namespace Apache.NMS.Stomp.Transport.Tcp
/// </summary>
public class TcpTransport : ITransport
{
private readonly object myLock = new object();
private readonly Socket socket;
protected readonly object myLock = new object();
protected readonly Socket socket;
private IWireFormat wireformat;
private BinaryReader socketReader;
private BinaryWriter socketWriter;
@@ -60,6 +60,11 @@ public TcpTransport(Uri uri, Socket socket, IWireFormat wireformat)
Dispose(false);
}

protected virtual Stream CreateSocketStream()
{
return new NetworkStream(socket);
}

/// <summary>
/// Method Start
/// </summary>
@@ -83,10 +88,11 @@ public void Start()

started = true;

// As reported in AMQ-988 it appears that NetworkStream is not thread safe
// so lets use an instance for each of the 2 streams
socketWriter = new BinaryWriter(new NetworkStream(socket));
socketReader = new BinaryReader(new NetworkStream(socket));
// Initialize our Read and Writer instances. Its not actually necessary
// to have two distinct NetworkStream instances but for now the TcpTransport
// will continue to do so for legacy reasons.
socketWriter = new BinaryWriter(CreateSocketStream());
socketReader = new BinaryReader(CreateSocketStream());

// now lets create the background read thread
readThread = new Thread(new ThreadStart(ReadLoop));
@@ -119,7 +119,7 @@ public ITransport CompositeConnect(Uri location, SetTransport setTransport)
IWireFormat wireformat = new StompWireFormat();
// Set wireformat. properties on the wireformat owned by the tcpTransport
URISupport.SetProperties(wireformat, map, "wireFormat.");
ITransport transport = new TcpTransport(location, socket, wireformat);
ITransport transport = DoCreateTransport(location, socket, wireformat);

wireformat.Transport = transport;

@@ -151,6 +151,15 @@ public ITransport CreateTransport(Uri location)

#endregion

/// <summary>
/// Override in a subclass to create the specific type of transport that is
/// being implemented.
/// </summary>
protected virtual ITransport DoCreateTransport(Uri location, Socket socket, IWireFormat wireFormat )
{
return new TcpTransport(location, socket, wireFormat);
}

// DISCUSSION: Caching host entries may not be the best strategy when using the
// failover protocol. The failover protocol needs to be very dynamic when looking
// up hostnames at runtime. If old hostname->IP mappings are kept around, this may
@@ -80,6 +80,9 @@ private static ITransportFactory CreateTransportFactory(Uri location)
case "tcp":
factory = new TcpTransportFactory();
break;
case "ssl":
factory = new SslTransportFactory();
break;
default:
throw new NMSConnectionException(String.Format("The transport {0} is not supported.", scheme));
}
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -31,6 +31,7 @@
<BootstrapperEnabled>true</BootstrapperEnabled>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>keyfile\NMSKey.snk</AssemblyOriginatorKeyFile>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -143,6 +144,8 @@
<Compile Include="src\main\csharp\Protocol\StompFrame.cs" />
<Compile Include="src\main\csharp\Protocol\IPrimitiveMapMarshaler.cs" />
<Compile Include="src\main\csharp\Protocol\XmlPrimitiveMapMarshaler.cs" />
<Compile Include="src\main\csharp\Transport\Tcp\SslTransport.cs" />
<Compile Include="src\main\csharp\Transport\Tcp\SslTransportFactory.cs" />
</ItemGroup>
<ItemGroup>
<None Include="keyfile\NMSKey.snk" />

0 comments on commit d85134b

Please sign in to comment.