Question

I am having a hard time running a M2Crypto SSLServer with EXPORT grade ciphers.

LOW/MEDIUM/HIGH grade ciphers work without any problems, but EXPORT just won't. Also, when OpenSSL is run in a server mode from a command line it accepts EXPORT grade ciphers without any problems.

So, either I am missing something or there is a problem in a M2Crypto module. Any help is appreciated.

Used python code (ssl-server.py) looks like this:

import M2Crypto
import socket

CERTFILE = "dummy_cert.pem"
KEYFILE = "dummy_key.pem"
PROTOCOL = "sslv3"
HOST = "0.0.0.0"
PORT = 4433

def main():
    print "[i] Initializing context ..."
    ctx = M2Crypto.SSL.Context(protocol=PROTOCOL, weak_crypto=True)
    ctx.load_cert_chain(certchainfile=CERTFILE, keyfile=KEYFILE)
    ctx.set_options(M2Crypto.m2.SSL_OP_ALL)
    ctx.set_cipher_list("ALL")

    print "[i] Initializing socket ..."
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind((HOST, PORT))
    sock.listen(1)
    conn, addr = sock.accept()

    print "[i] SSL handshake ..."
    ssl_conn = M2Crypto.SSL.Connection(ctx=ctx, sock=conn)
    ssl_conn.setup_ssl()
    try:
        ssl_conn_res = ssl_conn.accept_ssl()
    except Exception, ex:
        print "[x] SSL connection failed: '%s'" % str(ex)
    else:
        if ssl_conn_res == 1:
            print "[i] SSL connection accepted"
        else:
            print "[x] SSL handshake failed: '%s'" % ssl_conn.ssl_get_error(ssl_conn_res)

if __name__ == "__main__":
    main()

Symptoms are:

$ uname -a
Linux XYZ 2.6.38-15-generic #59-Ubuntu SMP Fri Apr 27 16:03:32 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=11.04
DISTRIB_CODENAME=natty
DISTRIB_DESCRIPTION="Ubuntu 11.04"

$ python -c "import M2Crypto;print M2Crypto.version_info"
(0, 20, 1)

$ openssl version
OpenSSL 0.9.8o 01 Jun 2010

1) NOT OK
SERVER (terminal 1): $ python ssl-server.py
CLIENT (terminal 2): $ openssl s_client -connect localhost:4433 -cipher EXPORT
CONNECTED(00000003)
28131:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:602:

2) OK
SERVER (terminal 1): $ openssl s_server -cert dummy_cert.pem -key dummy_key.pem -ssl3 -no_tls1 -no_ssl2 -cipher EXPORT
CLIENT (terminal 2): $ openssl s_client -connect localhost:4433 -cipher EXPORT
CONNECTED(00000003)
depth=0 C = BE, CN = www.example.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = BE, CN = www.example.com
verify error:num=27:certificate not trusted
verify return:1
depth=0 C = BE, CN = www.example.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/C=BE/CN=www.example.com
   i:/C=BE/CN=test-ca
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/C=BE/CN=www.example.com
issuer=/C=BE/CN=test-ca
---
No client certificate CA names sent
---
SSL handshake has read 1141 bytes and written 242 bytes
---
New, TLSv1/SSLv3, Cipher is EXP-EDH-RSA-DES-CBC-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: zlib compression
Expansion: zlib compression
SSL-Session:
    Protocol  : SSLv3
    Cipher    : EXP-EDH-RSA-DES-CBC-SHA
    Session-ID: B052D5D5A436F9A0B9D3FB24F2E32A8A06A0B6828230621C4CFAEB82A0A9AE0C
    Session-ID-ctx: 
    Master-Key:     47F6E3720D06518B961FE389F13BCDE42C37F703099ABBB9B3DA35383C420F519D4F4773D35E470CF6FF7BB243B29069
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Compression: 1 (zlib compression)
    Start Time: 1340644713
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

Content of a dummy_cert.pem is as follows:

-----BEGIN CERTIFICATE-----
MIICkTCCAfqgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAfMQswCQYDVQQGEwJCRTEQ
MA4GA1UEAxMHdGVzdC1jYTAeFw0xMjA1MDYwODQyNDlaFw0yMjA1MDMwODQyNDla
MCcxCzAJBgNVBAYTAkJFMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wgZ8wDQYJ
KoZIhvcNAQEBBQADgY0AMIGJAoGBAL7OBv9wRwtNjN984XSy22/rw6tHM6Lq/Ccf
NoHKbqwC+PsxgmgJJiGBGewrzBR42toqHJi7EjHhuvrgqV9s2duPQBAANh7tzY1h
6VekrwhIIt4o1h0F2KB16VXA8s918d+8pRGt2T11GUh/QT3m9yY1VzqdIBeAfklC
ET6ncPK/AgMBAAGjgdQwgdEwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAw
KwYJYIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYD
VR0OBBYEFNGQArEZPKprJTn7A64qEFfl0m4xME8GA1UdIwRIMEaAFFuITOUJlGrJ
9lKufs8cm1MpwXrroSOkITAfMQswCQYDVQQGEwJCRTEQMA4GA1UEAxMHdGVzdC1j
YYIJALimgW7YUgdrMAkGA1UdEgQCMAAwCQYDVR0RBAIwADANBgkqhkiG9w0BAQUF
AAOBgQDWh8A0eBxI9XHy68xdjFsk2oerJeV6qqlcmtPZgz3GlarRcWcKsRJOyLLL
dCOe7tY5isWQAoLt6XALzDWjbQkTJnxBaKHif1MIikuajaYKT7LA1MvFn50Qrm6n
f9hG7gvdTpm1rlPcs0qibp1vJVubkU51mT6JT4UnLfeVIjtL7Q==
-----END CERTIFICATE-----

Content of a dummy_key.pem is as follows:

-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQC+zgb/cEcLTYzffOF0sttv68OrRzOi6vwnHzaBym6sAvj7MYJo
CSYhgRnsK8wUeNraKhyYuxIx4br64KlfbNnbj0AQADYe7c2NYelXpK8ISCLeKNYd
BdigdelVwPLPdfHfvKURrdk9dRlIf0E95vcmNVc6nSAXgH5JQhE+p3DyvwIDAQAB
AoGBAIZldIRkP4Z0n2+j9OJQQUS6Wl7AjlyJDAc6cxhE0GOUzG+S1foVx6f92ZaC
2wLoha75zp691fkQuLWRnXu7nk9QwxQdOppKijIPHdL2cYtUc9UCedN5rExjpcOP
4Hjwf17YOxK2J0zzmG1djTBB47BKGUedSQ7E1QxGcrESS2XxAkEA+6ey2jy8etWi
QmCdJJIxXwKRVHCmt5LVwj+IOk/u3sr1AGfBm7spKGU3boCiFt4FmjGMax7B9r/e
zPaMb34guwJBAMIZX7Vv5gfjvWtgp6pyE/UkjRSOKBpuy9gyiqtLBJwehj/qsBqr
O6tFmjMFiudVusnVSrEFGAPLV52xf0U4580CQQDkEQ1UH2spX2dYBLslo6A+3NLc
1eMhx18WVgGd50cyfnkfzuh1vF8GjwR3jvhXBQvKvFDn284pU6YV1vNbL9F1AkEA
o2CwSwyRV3q+6i9Fchbr7aCCkBbIctdoBeclCeHvU2nuHsbwzMHtS9EeZmv365kh
zNoYMMDU4fy7FyVct2ua0QJASXtIwYKZ2CAP+lAQqfh+knRRqtqdLt4Lt0mpML5m
UtsECS8frKeF3mynXfsyRkvC8F2WFiJVJ3+D+y3zYNGlZg==
-----END RSA PRIVATE KEY-----

Update: at the low level handshake packets seem to be the same except that random[32] field making this even more strange.

SSL dump (ssldump -a -A -H -i lo) for both cases can be found here:

http://pastebin.com/YuC7d8zg (NOT OK case)

http://pastebin.com/U6YGQmv9 (OK case)

Was it helpful?

Solution

I needed the following two tweaks to the python script to make it work with export cipher suites:

PROTOCOL = "sslv23"
...
    print "[i] Initializing context ..."
    ctx = M2Crypto.SSL.Context(protocol=PROTOCOL, weak_crypto=True)
    ctx.load_cert_chain(certchainfile=CERTFILE, keyfile=KEYFILE)
    ctx.set_options(M2Crypto.m2.SSL_OP_ALL)
    ctx.set_tmp_rsa(M2Crypto.RSA.gen_key(512, 65537))
    ctx.set_cipher_list("ALL")

That is:

  1. Use SSLv23 as protocol identifier (SSLv2/v3 compat mode). Not sure why it is needed in this case, but it seems not to work otherwise.
  2. Set a temporary, ephemeral RSA key on the context using set_tmp_rsa(). This is required because with export ciphers, the provided (1024-bit) RSA key is only used for authentication (signing), while a temporary, export-crippled 512-bit RSA key is used for confidentiality (encryption). OpenSSL requires you to set up this key on the context (see the documentation of SSL_set_tmp_rsa()).

Weirdly enough, it also works in SSLv2-only mode (using -ssl2 on openssl s_client when testing) without setting a temporary RSA key (call to set_tmp_rsa commented out in the script). I have no idea why.

In general, some cipher suites require special keys be added to the context, e.g. suites using DH (group parameters) or ECDH (curve). To see exactly what is used for each cipher suite, openssl ciphers -v can be insightful, e.g.:

% openssl ciphers -v EXPORT
EXP-EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH(512)  Au=RSA  Enc=DES(40)   Mac=SHA1 export
EXP-EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH(512)  Au=DSS  Enc=DES(40)   Mac=SHA1 export
EXP-ADH-DES-CBC-SHA     SSLv3 Kx=DH(512)  Au=None Enc=DES(40)   Mac=SHA1 export
EXP-DES-CBC-SHA         SSLv3 Kx=RSA(512) Au=RSA  Enc=DES(40)   Mac=SHA1 export
EXP-RC2-CBC-MD5         SSLv3 Kx=RSA(512) Au=RSA  Enc=RC2(40)   Mac=MD5  export
EXP-RC2-CBC-MD5         SSLv2 Kx=RSA(512) Au=RSA  Enc=RC2(40)   Mac=MD5  export
EXP-ADH-RC4-MD5         SSLv3 Kx=DH(512)  Au=None Enc=RC4(40)   Mac=MD5  export
EXP-RC4-MD5             SSLv3 Kx=RSA(512) Au=RSA  Enc=RC4(40)   Mac=MD5  export
EXP-RC4-MD5             SSLv2 Kx=RSA(512) Au=RSA  Enc=RC4(40)   Mac=MD5  export

EDIT in respone to the question about DSS cipher suites:

DSS/DSA cipher suites need DH parameters, and of course a DSS/DSA based server certificate instead of (only) an RSA one. This is true not only for export cipher suites, but for all suites using DSS/DSA for authenticity. DSS/DSA can by design only be used for signatures, not for encryption, in order to allow for export into untrusted countries. Because DSS/DSA can only be used for signatures, it needs an ephemeral Diffie-Hellman key exchange to establish a shared session key. That's what the EDH in the cipher suite stands for. To set up DH parameters, you'd use the M2Crypto equivalents of the OpenSSL SSL_set_tmp_dh() API.

Note that OpenSSL allows to load both an RSA and a DSA/DSS cert/keypair into the same SSL context.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top