1 """MyProxy Web Service - web services client package
2
3 """
4 __author__ = "P J Kershaw"
5 __date__ = "28/05/12"
6 __copyright__ = "(C) 2012 Science and Technology Facilities Council"
7 __license__ = "BSD - see LICENSE file in top-level directory"
8 __contact__ = "Philip.Kershaw@stfc.ac.uk"
9 __revision__ = '$Id$'
10 import logging
11 log = logging.getLogger(__name__)
12 import base64
13 import os
14 import errno
15 import urllib2
16 from urlparse import urlparse, urlunparse
17
18 from OpenSSL import SSL, crypto
19 from ndg.httpsclient.utils import (_should_use_proxy, fetch_stream_from_url,
20 Configuration)
21 from ndg.httpsclient.ssl_context_util import make_ssl_context
22 from ndg.httpsclient.urllib2_build_opener import build_opener
23
24 testvar = True
27 PRIKEY_NBITS = 2048
28 MESSAGE_DIGEST_TYPE = "md5"
29 CERT_REQ_POST_PARAM_KEYNAME = 'certificate_request'
30 TRUSTED_CERTS_FIELDNAME = 'TRUSTED_CERTS'
31 TRUSTED_CERTS_FILEDATA_FIELDNAME_PREFIX = 'FILEDATA_'
32
34 self.__ca_cert_dir = None
35 self.timeout = 500
36
37 @property
39 return self.__ca_cert_dir
40
41 @ca_cert_dir.setter
43 if not isinstance(val, basestring):
44 raise TypeError('Expecting string type for "ca_cert_dir"; got %r' %
45 type(val))
46
47 self.__ca_cert_dir = val
48
49 @staticmethod
51 """Generate key pair and return as PEM encoded string
52 @type n_bits_for_key: int
53 @param n_bits_for_key: number of bits for private key generation -
54 default is 2048
55 @rtype: OpenSSL.crypto.PKey
56 @return: public/private key pair
57 """
58 key_pair = crypto.PKey()
59 key_pair.generate_key(crypto.TYPE_RSA, n_bits_for_key)
60
61 return key_pair
62
63 @staticmethod
65 """Create a certificate request.
66
67 @type CN: basestring
68 @param CN: Common Name for certificate - effectively the same as the
69 username for the MyProxy credential
70 @type keyPair: string/None
71 @param keyPair: public/private key pair
72 @type messageDigest: basestring
73 @param messageDigest: message digest type - default is MD5
74 @rtype: base string
75 @return certificate request PEM text and private key PEM text
76 """
77
78
79
80 cert_req = crypto.X509Req()
81
82
83 cert_req.set_pubkey(key_pair)
84
85
86 cert_req.sign(key_pair, message_digest)
87
88 cert_req = crypto.dump_certificate_request(crypto.FILETYPE_PEM,
89 cert_req)
90
91 return cert_req
92
93 - def logon(self, username, password, server_url, proxies=None, no_proxy=None,
94 cert_life_time=86400, ssl_ctx=None):
95 """Obtain a new certificate"""
96 if ssl_ctx is None:
97 ssl_ctx = make_ssl_context(ca_dir=self.ca_cert_dir, verify_peer=True,
98 url=server_url,
99 method=SSL.SSLv3_METHOD)
100
101
102 password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
103
104
105 parsed_url = urlparse(server_url)
106 base_url = urlunparse(parsed_url[0:2] + ('/', '', '', ''))
107
108
109
110 password_mgr.add_password(None, base_url, username, password)
111
112 handlers = [urllib2.HTTPBasicAuthHandler(password_mgr)]
113
114 key_pair = self.__class__.create_key_pair()
115 cert_req = self.__class__.create_cert_req(key_pair)
116
117
118 encoded_cert_req = cert_req.replace('+', '%2B')
119 req = "%s=%s\n" % (self.__class__.CERT_REQ_POST_PARAM_KEYNAME,
120 encoded_cert_req)
121 config = Configuration(ssl_ctx, True)
122 res = fetch_stream_from_url(server_url, config, data=req,
123 handlers=handlers)
124
125 return res
126
127 - def get_trustroots(self, server_url, write_to_ca_cert_dir=False,
128 bootstrap=False):
129 """Get trustroots"""
130 raise NotImplementedError('To be completed in a subsequent release')
131 prefix = self.__class__.TRUSTED_CERTS_FILEDATA_FIELDNAME_PREFIX
132 field_name = self.__class__.TRUSTED_CERTS_FIELDNAME
133 file_data = {}
134
135 files_dict = dict([(k.split(prefix, 1)[1], base64.b64decode(v))
136 for k, v in file_data.items() if k != field_name])
137
138 if write_to_ca_cert_dir:
139
140 try:
141 os.makedirs(self.ca_cert_dir)
142 except OSError, e:
143
144 if e.errno != errno.EEXIST:
145 raise
146
147 for file_name, file_contents in files_dict.items():
148 file_path = os.path.join(self.ca_cert_dir, file_name)
149 open(file_path, 'wb').write(file_contents)
150
151 return files_dict
152