Hi Haskellers,
I am trying to connect a Java client to a Haskell server using the
Haskell tls package, and things are not working out for me. There is a
lot of steps involved and I do not know what I am doing wrong, so this
is a long message. But first I create a private/public key-pair:
> openssl genrsa -out privkey.pem 2048
then I make a self-signed certificate:
> openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
> Country Name (2 letter code) [AU]:
> State or Province Name (full name) [Some-State]:
> Locality Name (eg, city) []:
> Organization Name (eg, company) [Internet Widgits Pty Ltd]:
> Organizational Unit Name (eg, section) []:
> Common Name (eg, YOUR name) []:192.168.1.6
> Email Address []:
then I convert the certificate to DER format and stuff it into a Java
keystore:
> openssl x509 -in cacert.pem -out cert.der -outform DER
> keytool -keystore myKeystore.store -importcert -storepass foobar -keypass
> foobar -file cert.der
now I start the Haskell server:
> ghc -hide-package monads-tf Server.hs -e main
and then the Java client:
> javac Client.java
> java -Djavax.net.debug=all -Djavax.net.ssl.trustStore=myKeystore.store
> -Djavax.net.ssl.trustStorePassword=foobar Client >JavaClientOutput.txt 2>&1
The server output is:
> <interactive>: user error (unexpected type received. expecting handshake ++
> Left (Error_Packet "invalid type"))
and not "Hello world" as expected.
The client output is very long, but the most interesting part is
properly:
> main, received EOFException: error
> main, handling exception: javax.net.ssl.SSLHandshakeException: Remote host
> closed connection during handshake
> main, SEND TLSv1 ALERT: fatal, description = handshake_failure
I have attached the Haskell server, the Java client and the full java
output. Hope somebody can help figure out what I do wrong.
I am using the Haskell tsl package version 0.3.1. And I run Debian
Linux.
I also tried connecting a Java client to a Java server. First create
server keystore:
> openssl pkcs8 -topk8 -nocrypt -in privkey.pem -inform PEM -out privkey.der
> -outform DER
> java -Dkeystore=myServerKeystore.store ImportKey privkey.der cert.der
ImportKey.java can be found here
http://www.agentbob.info/agentbob/79-AB.html .
then start Java server:
> javac JavaServer.java
> java -Djavax.net.ssl.keyStore=myServerKeystore.store
> -Djavax.net.ssl.keyStorePassword=importkey JavaServer
and run the client again:
> java -Djavax.net.debug=all -Djavax.net.ssl.trustStore=myKeystore.store
> -Djavax.net.ssl.trustStorePassword=foobar Client
and the server outputs:
> Hello world
as expected. Thus I think the certificates are fine, and the Java client
is fine. But what am I doing wrong in the Haskell server?
I have attached JavaServer.java.
Regards,
Mads Lindstrøm
import javax.net.*;
import java.net.*;
import javax.net.ssl.*;
import java.io.*;
class Client {
public static void main(String[] args) {
try {
int port = 8000;
String hostname = "192.168.1.6"; // Insert your localhost ip
SocketFactory socketFactory = SSLSocketFactory.getDefault();
Socket socket = socketFactory.createSocket(hostname, port);
// Create streams to securely send and receive data to the server
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
PrintWriter writer = new PrintWriter(out);
writer.println("Hello world");
// Read from in and write to out...
// Close the socket
writer.close();
in.close();
out.close();
} catch(IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
import javax.net.*;
import java.net.*;
import javax.net.ssl.*;
import java.io.*;
class JavaServer {
public static void main(String[] args) {
try {
int port = 8000;
String hostname = "192.168.1.6"; // Insert your localhost ip
ServerSocketFactory ssocketFactory = SSLServerSocketFactory.getDefault();
ServerSocket ssocket = ssocketFactory.createServerSocket(port);
// Listen for connections
Socket socket = ssocket.accept();
// Create streams to securely send and receive data to the client
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
System.out.println(reader.readLine());
// Read from in and write to out...
// Close the socket
reader.close();
in.close();
out.close();
} catch(IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
} -- ghci -hide-package monads-tf Server.hs
module Main where
import qualified Control.Monad.Trans as Trans
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import qualified Data.Certificate.PEM as PEM
import qualified Data.Certificate.Key as Key
import qualified Data.Certificate.X509 as X509
import qualified Network.TLS.Server as S
import qualified Network.TLS.Struct as Struct
import qualified Network.TLS.Cipher as Cipher
import qualified Network.TLS.SRandom as SRandom
import qualified Control.Exception as E
import qualified Network as Net
import qualified Network.Socket as Socket
import qualified IO as IO
import qualified Control.Concurrent as Concurrent
main :: IO ()
main = do
pk <- readPrivateKey "privkey.pem"
(certdata, cert) <- readCertificate "cacert.pem"
listenForClients (certdata, cert, snd pk)
return ()
type SpCert = (B.ByteString, X509.Certificate, Key.PrivateKey)
listenForClients :: SpCert -> IO ()
listenForClients spCert
= Net.withSocketsDo $ E.bracket aquireSocket Net.sClose loop where
aquireSocket =
do socket <- Net.listenOn $ Net.PortNumber 8000
Socket.setSocketOption socket Socket.ReuseAddr 1
return socket
loop socket =
do (handle, _, _) <- Net.accept socket
sslListen spCert handle
loop socket
sslListen :: SpCert -> IO.Handle -> IO ()
sslListen spCert handle = do
serverRNG <- getRandomGen
let serverState = makeValidParams spCert
let tlsServer = do
S.listen handle
d <- S.recvData handle
Trans.lift $ print d
return ()
Concurrent.forkIO (S.runTLSServer tlsServer serverState serverRNG >> return ())
return ()
getRandomGen :: IO SRandom.SRandomGen
getRandomGen = SRandom.makeSRandomGen >>= either (fail . show) (return . id)
makeValidParams :: SpCert -> S.TLSServerParams
makeValidParams spCert =
S.TLSServerParams
supportedVersions
[] -- session
supportedCiphers
(Just spCert) -- server certificate
False -- check for client certificate
(S.TLSServerCallbacks { S.cbCertificates = Nothing })
supportedVersions :: [Struct.Version]
supportedVersions = [Struct.SSL3, Struct.TLS10, Struct.TLS11]
supportedCiphers :: [Cipher.Cipher]
supportedCiphers =
[ Cipher.cipher_AES128_SHA1
, Cipher.cipher_AES256_SHA1
, Cipher.cipher_RC4_128_MD5
, Cipher.cipher_RC4_128_SHA1
]
readCertificate :: FilePath -> IO (B.ByteString, X509.Certificate)
readCertificate filepath = do
content <- B.readFile filepath
let certdata = case PEM.parsePEMCert content of
Nothing -> error ("no valid certificate section")
Just x -> x
let cert = case X509.decodeCertificate $ L.fromChunks [certdata] of
Left err -> error ("cannot decode certificate: " ++ err)
Right x -> x
return (certdata, cert)
readPrivateKey :: FilePath -> IO (L.ByteString, Key.PrivateKey)
readPrivateKey filepath = do
content <- B.readFile filepath
let pkdata = case PEM.parsePEMKeyRSA content of
Nothing -> error ("no valid RSA key section")
Just x -> L.fromChunks [x]
let pk = case Key.decodePrivateKey pkdata of
Left err -> error ("cannot decode key: " ++ err)
Right x -> x
return (pkdata, pk)
keyStore is :
keyStore type is : jks
keyStore provider is :
init keystore
init keymanager of type SunX509
trustStore is: myKeystore.store
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
Subject: CN=192.168.1.6, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU
Issuer: CN=192.168.1.6, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU
Algorithm: RSA; Serial number: 0xafd8b93f9f4ca3d7
Valid from Sun Dec 12 19:50:14 CET 2010 until Wed Dec 11 19:50:14 CET 2013
trigger seeding of SecureRandom
done seeding SecureRandom
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1
RandomCookie: GMT: 1292180106 bytes = { 40, 135, 73, 129, 198, 116, 247, 112,
56, 12, 229, 94, 70, 112, 36, 197, 0, 80, 139, 101, 102, 152, 214, 85, 31, 198,
224, 53 }
Session ID: {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA,
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA,
SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5,
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
***
[write] MD5 and SHA1 hashes: len = 75
0000: 01 00 00 47 03 01 4D 05 1B 8A 28 87 49 81 C6 74 ...G..M...(.I..t
0010: F7 70 38 0C E5 5E 46 70 24 C5 00 50 8B 65 66 98 .p8..^Fp$..P.ef.
0020: D6 55 1F C6 E0 35 00 00 20 00 04 00 05 00 2F 00 .U...5.. ...../.
0030: 33 00 32 00 0A 00 16 00 13 00 09 00 15 00 12 00 3.2.............
0040: 03 00 08 00 14 00 11 00 FF 01 00 ...........
main, WRITE: TLSv1 Handshake, length = 75
[write] MD5 and SHA1 hashes: len = 101
0000: 01 03 01 00 3C 00 00 00 20 00 00 04 01 00 80 00 ....<... .......
0010: 00 05 00 00 2F 00 00 33 00 00 32 00 00 0A 07 00 ..../..3..2.....
0020: C0 00 00 16 00 00 13 00 00 09 06 00 40 00 00 15 ............@...
0030: 00 00 12 00 00 03 02 00 80 00 00 08 00 00 14 00 ................
0040: 00 11 00 00 FF 4D 05 1B 8A 28 87 49 81 C6 74 F7 .....M...(.I..t.
0050: 70 38 0C E5 5E 46 70 24 C5 00 50 8B 65 66 98 D6 p8..^Fp$..P.ef..
0060: 55 1F C6 E0 35 U...5
main, WRITE: SSLv2 client hello message, length = 101
[Raw write]: length = 103
0000: 80 65 01 03 01 00 3C 00 00 00 20 00 00 04 01 00 .e....<... .....
0010: 80 00 00 05 00 00 2F 00 00 33 00 00 32 00 00 0A ....../..3..2...
0020: 07 00 C0 00 00 16 00 00 13 00 00 09 06 00 40 00 ..............@.
0030: 00 15 00 00 12 00 00 03 02 00 80 00 00 08 00 00 ................
0040: 14 00 00 11 00 00 FF 4D 05 1B 8A 28 87 49 81 C6 .......M...(.I..
0050: 74 F7 70 38 0C E5 5E 46 70 24 C5 00 50 8B 65 66 t.p8..^Fp$..P.ef
0060: 98 D6 55 1F C6 E0 35 ..U...5
main, received EOFException: error
main, handling exception: javax.net.ssl.SSLHandshakeException: Remote host
closed connection during handshake
main, SEND TLSv1 ALERT: fatal, description = handshake_failure
main, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 28 ......(
main, called closeSocket()
main, called close()
main, called closeInternal(true)
main, called close()
main, called closeInternal(true)
_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe