views:

301

answers:

7

I am using M2Crypto-0.20.2. I want to use engine_pkcs11 from the OpenSC project and the Aladdin PKI client for token based authentication making xmlrpc calls over ssl.

from M2Crypto import Engine

Engine.load_dynamic()
dynamic = Engine.Engine('dynamic')
# Load the engine_pkcs from the OpenSC project
dynamic.ctrl_cmd_string("SO_PATH", "/usr/local/ssl/lib/engines/engine_pkcs11.so")
Engine.cleanup()

Engine.load_dynamic()
# Load the Aladdin PKI Client
aladdin = Engine.Engine('dynamic')
aladdin.ctrl_cmd_string("SO_PATH", "/usr/lib/libeTPkcs11.so")

key = aladdin.load_private_key("PIN","password")

This is the error I receive:

key = pkcs.load_private_key("PIN","eT0ken")
File "/usr/local/lib/python2.4/site-packages/M2Crypto/Engine.py", line 70, in load_private_key
    return self._engine_load_key(m2.engine_load_private_key, name, pin)
File "/usr/local/lib/python2.4/site-packages/M2Crypto/Engine.py", line 60, in _engine_load_key
    raise EngineError(Err.get_error())
M2Crypto.Engine.EngineError: 23730:error:26096075:engine routines:ENGINE_load_private_key:not initialised:eng_pkey.c:112:

For load_private_key(), what should be passed as the first argument? The M2Crypto documentation does not explain it.

I don't get any errors loading the engines, but I'm not sure if I'm loading them correctly. It seems like the engine ID has to be a specific name but I don't find that list anywhere. 'dynamic' is working for me.

Any help would be appreciated!

A: 

I have exactly the same problem... I don't know what the "initialization" means...? Is anyone can show a piece of code using private key from smartcard/token coded with Python ?

Erlo
Someone on the OpenSC mailing list posted a link to this yesterday --> http://fronted.pastebin.ca/549820This is quite old though. As far as I can tell some of the m2 module methods are not available anymore. I'm using very similiar ones from the Engine module.
Becky
A: 

Looking at the pastebin link Becky provided, I believe it translates to something like this in the new API:

from M2Crypto import Engine, m2

dynamic = Engine.load_dynamic_engine("pkcs11", "/Users/martin/prefix/lib/engines/engine_pkcs11.so")

pkcs11 = Engine.Engine("pkcs11")

pkcs11.ctrl_cmd_string("MODULE_PATH", "/Library/OpenSC/lib/opensc-pkcs11.so")

r = pkcs11.ctrl_cmd_string("PIN", sys.argv[1])

key = pkcs11.load_private_key("id_01")

So I am betting that if you substitute "/Users/martin/prefix/lib/engines/engine_pkcs11.so" with "/usr/local/ssl/lib/engines/engine_pkcs11.so" and "/Library/OpenSC/lib/opensc-pkcs11.so" with "/usr/lib/libeTPkcs11.so" you might get it to work with Aladdin.

Heikki Toivonen
A: 

Hello,

That is exactly the code I've tried. But It ended with the following error:

Traceback (most recent call last):
  File "prog9.py", line 13, in <module>
    key = pkcs11.load_private_key("id_45")
  File "/usr/lib/pymodules/python2.5/M2Crypto/Engine.py", line 70, in load_private_key
    return self._engine_load_key(m2.engine_load_private_key, name, pin)
  File "/usr/lib/pymodules/python2.5/M2Crypto/Engine.py", line 60, in _engine_load_key
    raise EngineError(Err.get_error())
M2Crypto.Engine.EngineError: 11814:error:26096075:engine outines:ENGINE_load_private_key:not initialised:eng_pkey.c:112:

I'm using OpenSC PKCS11 lib, not aladdin lib. But I don't think the problem is closed.

Erlo
A: 

I tried the code that Heikki suggested (minus one line) and got the same error as Erlo. For load_private_key(), how do I know what to put in for the argument?

dynamic = Engine.load_dynamic_engine("pkcs11", "/usr/local/ssl/lib/engines/engine_pkcs11.so")
#  m2.engine_free(dynamic) this line gave me an error TypeError: in method 'engine_free', argument 1 of type 'ENGINE *'

pkcs11 = Engine.Engine("pkcs11")
pkcs11.ctrl_cmd_string("MODULE_PATH", "/usr/lib/libeTPkcs11.so")

r = pkcs11.ctrl_cmd_string("PIN", "password")

key = pkcs11.load_private_key("id_01")
Becky
A: 

I think the problem is not really the "load_private_key()". It's like something is missing between "MODULE_PATH" definition and the load_private_key() call. What happen if you remplace "/usr/lib/libeTPkcs11.so" by a wrong path ? In my case I have no error related to this.

I've run "pcscd" in foreground with high debug level, there is no call to smartcard during the python execution... So definitly, I don't understand what's wrong...

The equivalent in "openssl" is using "-pre" command. The "-pre" (by opposite to the "-post") are command sent to the engine before loading. Perhaps we need to call a methode which "load" the engine after all "ctrl_cmd_string" calls ?? ...

Lost :-/

Erlo
I'm lost too :)Ahh, yes if I put in a wrong path I still get the same 'not initialized' error.Do you know when to use load_dynamic_engine() versus load_dynamic()?The old example code I posted a link to has a m2.engine_init() after the ctrl_cmd_strings(). I haven't found anything equivalent to that yet.I'll keep you posted if I learn anything.
Becky
I think before the pin is sent, the engine needs to be initialized and this is not happening.The openssl engine documentation talks about structural and functional references. To use an engines functionality, you need a functional reference which is obtained by by calling the ENGINE_init()function. I don't know how this translates to the python/m2crypto world.
Becky
Please take a look at the Engine.py and _engine.i files. m2.engine_init() simply sets up the EngineError exception class. It happens automatically when you import Engine in the new API.
Heikki Toivonen
+2  A: 

Found !!!!

Yes, exactly the way where I came from.

So, actually the ENGINE_init() is not implemented in M2Crypto.Engine. So, only one solution: patching!!! (very small...) so I've created a new Engine method (in Engine.py)

def engine_initz(self):
        """Return engine name"""
        return m2.engine_initz(self._ptr)

Why engine_initz ? because engine_init is already define in SWIG/_engine.i,:

void engine_init(PyObject *engine_err) {
    Py_INCREF(engine_err);
    _engine_err = engine_err;
}

I don't really know what is done, so I've prefered creating a new one... So I've just added the following to SWIG/_engine.i:

%rename(engine_initz) ENGINE_init;
extern int ENGINE_init(ENGINE *);

And recompile the __m2crypto.so, now just add a "pkcs11.engine_initz()" before launching the private key, and it works.....

Erlo
Good work! I followed your instructions for patching M2Crypto and was able to get past the initilization error as well. Thank you.
Becky
Good job! Could you file a bug on M2Crypto? Also, could you mention at what point does `ENGINE_init()` need to be called?
Heikki Toivonen
I changed the current m2.engine_init to engine_init_error, exposed m2.engine_init and engine_finish and added init and finish methods to Engine class. Checked into M2Crypto trunk, please test before I make next release.
Heikki Toivonen
+1  A: 

I don't know what and why the engine_init code present in current M2Crypto is supposed to do. Exposing ENGINE_init() as engine_init2 with the following patch to M2Crypto helps:

Index: SWIG/_engine.i
===================================================================
--- SWIG/_engine.i  (revision 719)
+++ SWIG/_engine.i  (working copy)
@@ -44,6 +44,9 @@
 %rename(engine_free) ENGINE_free;
 extern int ENGINE_free(ENGINE *);

+%rename(engine_init2) ENGINE_init;
+extern int ENGINE_init(ENGINE *);
+
 /*
  * Engine id/name functions
  */

After this, the following code takes me further (but urllib does not fully work for me currently):

import sys, os, time, cgi, urllib, urlparse
from M2Crypto import m2urllib2 as urllib2
from M2Crypto import m2, SSL, Engine

# load dynamic engine
e = Engine.load_dynamic_engine("pkcs11", "/Users/martin/prefix/lib/engines/engine_pkcs11.so")
pk = Engine.Engine("pkcs11")
pk.ctrl_cmd_string("MODULE_PATH", "/Library/OpenSC/lib/opensc-pkcs11.so")

m2.engine_init2(m2.engine_by_id("pkcs11")) # This makes the trick

cert = e.load_certificate("slot_01-id_01")
key = e.load_private_key("slot_01-id_01", sys.argv[1])

ctx = SSL.Context("sslv23")
ctx.set_cipher_list("HIGH:!aNULL:!eNULL:@STRENGTH")
ctx.set_session_id_ctx("foobar")
m2.ssl_ctx_use_x509(ctx.ctx, cert.x509)
m2.ssl_ctx_use_pkey_privkey(ctx.ctx, key.pkey)

opener = urllib2.build_opener(ctx)
urllib2.install_opener(opener)
martin