diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index d25c172..a030cb0 100755 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -234,6 +234,9 @@ class Sfi: parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="verbose mode") + parser.add_option("-P", "--passphrase", + dest="passphrase", default=None, + help="private key passphrase") parser.add_option("-D", "--debug", action="store_true", dest="debug", default=False, help="Debug (xml-rpc) protocol messages") @@ -309,7 +312,7 @@ class Sfi: # Get key and certificate key_file = self.get_key_file() cert_file = self.get_cert_file(key_file) - self.key = Keypair(filename=key_file) + self.key = Keypair(filename=key_file, passphrase=self.options.passphrase) self.key_file = key_file self.cert_file = cert_file self.cert = Certificate(filename=cert_file) @@ -352,7 +355,7 @@ class Sfi: if (os.path.isfile(file)): return file else: - k = Keypair(filename=key_file) + k = Keypair(filename=key_file, passphrase=self.options.passphrase) cert = Certificate(subject=self.user) cert.set_pubkey(k) cert.set_issuer(k, self.user) diff --git a/sfa/trust/certificate.py b/sfa/trust/certificate.py index 64ac865..622fbb5 100644 --- a/sfa/trust/certificate.py +++ b/sfa/trust/certificate.py @@ -104,7 +104,8 @@ class Keypair: # @param string If string!=None, load the keypair from the string (PEM) # @param filename If filename!=None, load the keypair from the file - def __init__(self, create=False, string=None, filename=None): + def __init__(self, create=False, string=None, filename=None, passphrase=None): + self.passphrase = passphrase if create: self.create() if string: @@ -137,8 +138,14 @@ class Keypair: # Load the private key from a string. Implicitly the private key includes the public key. def load_from_string(self, string): - self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string) - self.m2key = M2Crypto.EVP.load_key_string(string) + print self.passphrase + if self.passphrase: + self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string, self.passphrase) + self.m2key = M2Crypto.EVP.load_key_string(string, callback=lambda x: self.passphrase) + else: + self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string) + self.m2key = M2Crypto.EVP.load_key_string(string) + ## # Load the public key from a string. No private key is loaded. diff --git a/sfa/util/xmlrpcprotocol.py b/sfa/util/xmlrpcprotocol.py index afba6f3..137a543 100644 --- a/sfa/util/xmlrpcprotocol.py +++ b/sfa/util/xmlrpcprotocol.py @@ -1,6 +1,10 @@ # XMLRPC-specific code for SFA Client import xmlrpclib +import tempfile +import socket +import httplib +from OpenSSL import crypto, SSL ## # ServerException, ExceptionUnmarshaller @@ -23,9 +27,60 @@ class ExceptionUnmarshaller(xmlrpclib.Unmarshaller): # # A transport for XMLRPC that works on top of HTTPS +class XMLRPCHTTPSConnection(httplib.HTTPSConnection): + def __init__(self, host, passphrase, port=None, key_file=None, cert_file=None, + strict=None): + httplib.HTTPSConnection.__init__(self, host, port, key_file, cert_file, strict) + self.passphrase = passphrase + + def connect(self): + """Connect to the host and port specified in __init__.""" + msg = "getaddrinfo returns an empty list" + + ctx = None + if self.passphrase: + ctx = SSL.Context(SSL.SSLv23_METHOD) + ctx.set_passwd_cb(lambda x,y,z: self.passphrase) + ctx.use_privatekey_file(self.key_file) + ctx.use_certificate_file(self.cert_file) + + for res in socket.getaddrinfo(self.host, self.port, 0, + socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + try: + if ctx: + self.sock = SSL.Connection(ctx, socket.socket(af, socktype, proto)) + else: + self.sock = socket.socket(af, socktype, proto) + if self.debuglevel > 0: + print "connect: (%s, %s)" % (self.host, self.port) + self.sock.connect(sa) + except socket.error, msg: + if self.debuglevel > 0: + print 'connect fail:', (self.host, self.port) + if self.sock: + self.sock.close() + self.sock = None + continue + break + if not self.sock: + raise socket.error, msg + +class XMLRPCHTTPS(httplib.HTTPS): + def __init__(self, passphrase, host='', port=None, key_file=None, cert_file=None, strict=None): + if port == 0: + port = None + self._setup(XMLRPCHTTPSConnection(host, passphrase, port, + key_file, cert_file, strict)) + self.key_file = key_file + self.cert_file = cert_file + + + class XMLRPCTransport(xmlrpclib.Transport): key_file = None cert_file = None + passphrase = None def make_connection(self, host): # create a HTTPS connection object from a host descriptor # host may be a string, or a (host, x509-dict) tuple @@ -38,7 +93,11 @@ class XMLRPCTransport(xmlrpclib.Transport): "your version of httplib doesn't support HTTPS" ) else: - return httplib.HTTPS(host, None, key_file=self.key_file, cert_file=self.cert_file) #**(x509 or {})) + if self.passphrase: + return XMLRPCHTTPS(self.passphrase, host, None, + key_file=self.key_file, cert_file=self.cert_file) + else: + return httplib.HTTPS(host, None, key_file=self.key_file, cert_file=self.cert_file) #**(x509 or {})) def getparser(self): unmarshaller = ExceptionUnmarshaller() @@ -61,6 +120,7 @@ class XMLRPCServerProxy(xmlrpclib.ServerProxy): def get_server(url, key_file, cert_file, options=None): transport = XMLRPCTransport() + transport.passphrase = options.passphrase transport.key_file = key_file transport.cert_file = cert_file