summaryrefslogtreecommitdiffstats
path: root/base/common/python
diff options
context:
space:
mode:
Diffstat (limited to 'base/common/python')
-rw-r--r--base/common/python/pki/cert.py57
-rw-r--r--base/common/python/pki/cryptoutil.py175
-rw-r--r--base/common/python/pki/key.py253
-rw-r--r--base/common/python/pki/kraclient.py442
-rw-r--r--base/common/python/pki/systemcert.py46
5 files changed, 554 insertions, 419 deletions
diff --git a/base/common/python/pki/cert.py b/base/common/python/pki/cert.py
index 6b8994e15..0a720a5e1 100644
--- a/base/common/python/pki/cert.py
+++ b/base/common/python/pki/cert.py
@@ -1,11 +1,13 @@
#!/usr/bin/python
'''
-Created on Aug 27, 2013
+Created on Feb 13, 2014
+Note: The implementation in this file has not been completed and is not tested.
+This note should be removed when testing/implementation is complete.
@author: akoneru
'''
import pki.client as client
-import pki.encoder as e
+import pki.encoder as encoder
import json
import types
@@ -32,11 +34,13 @@ class CertData(object):
''' Constructor '''
self.Encoded = None
- def from_dict(self, attr_list):
+ @classmethod
+ def from_dict(cls, attr_list):
''' Return CertData object from JSON dict '''
+ cert_data = cls()
for key in attr_list:
- setattr(self, key, attr_list[key])
- return self
+ setattr(cert_data, key, attr_list[key])
+ return cert_data
class CertDataInfo(object):
'''
@@ -58,11 +62,13 @@ class CertDataInfo(object):
self.issuedOn = None
self.issuedBy = None
- def from_dict(self, attr_list):
+ @classmethod
+ def from_dict(cls, attr_list):
''' Return CertDataInfo object from JSON dict '''
+ cert_data_info = cls()
for key in attr_list:
- setattr(self, key, attr_list[key])
- return self
+ setattr(cert_data_info, key, attr_list[key])
+ return cert_data_info
class CertDataInfos(object):
'''
@@ -75,17 +81,17 @@ class CertDataInfos(object):
self.certInfoList = []
self.links = []
- def decode_from_json(self, json_value):
+ @classmethod
+ def from_json(cls, json_value):
''' Populate object from JSON input '''
+ ret = cls()
cert_infos = json_value['CertDataInfo']
if not isinstance(cert_infos, types.ListType):
- certInfo = CertDataInfo()
- self.certInfoList.append(certInfo.from_dict(cert_infos))
+ ret.certInfoList.append(CertDataInfo.from_dict(cert_infos))
else:
for cert_info in cert_infos:
- cert_data_info = CertDataInfo()
- cert_data_info.from_dict(cert_info)
- self.certInfoList.append(cert_data_info)
+ ret.certInfoList.append(CertDataInfo.from_dict(cert_info))
+ return ret
class CertSearchRequest(object):
@@ -134,7 +140,7 @@ class CertSearchRequest(object):
self.certTypeInUse = False
-class CertResource(object):
+class CertClient(object):
'''
Class encapsulating and mirroring the functionality in the CertResouce Java interface class
defining the REST API for Certificate resources.
@@ -153,8 +159,7 @@ class CertResource(object):
''' Return a CertData object for a particular certificate. '''
url = self.cert_url + '/' + str(cert_id.id)
response = self.connection.get(url, self.headers)
- e.TYPES['CertData'] = CertData()
- certData = e.CustomTypeDecoder(response.json())
+ certData = encoder.CustomTypeDecoder(response.json())
return certData
def listCerts(self, status = None):
@@ -171,13 +176,10 @@ class CertResource(object):
def searchCerts(self, cert_search_request):
''' Return a CertDataInfos object containing the results of a cert search.'''
url = self.cert_url + '/search'
- e.TYPES['CertSearchRequest'] = CertSearchRequest
- searchRequest = json.dumps(cert_search_request, cls=e.CustomTypeEncoder)
+ searchRequest = json.dumps(cert_search_request, cls=encoder.CustomTypeEncoder)
r = self.connection.post(url, searchRequest, self.headers)
print r.json()['CertDataInfos']
- cdis = CertDataInfos()
- cdis.decode_from_json(r.json()['CertDataInfos'])
- return cdis
+ return CertDataInfos.from_json(r.json()['CertDataInfos'])
def getCerts(self, cert_search_request):
''' Doctring needed here. '''
@@ -199,19 +201,14 @@ class CertResource(object):
''' Doc string needed here '''
pass
+encoder.NOTYPES['CertData'] = CertData
+encoder.NOTYPES['CertSearchRequest'] = CertSearchRequest
- def get_transport_cert(self):
- ''' Return transport certificate '''
- url = '/rest/config/cert/transport'
- response = self.connection.get(url, self.headers)
- certData = CertData()
- certData.Encoded = response.json()['Encoded']
- return certData.Encoded
def main():
connection = client.PKIConnection('http', 'localhost', '8080', 'ca')
connection.authenticate('caadmin', 'Secret123')
- certResource = CertResource(connection)
+ certResource = CertClient(connection)
cert = certResource.getCert(CertId('0x6'))
print cert
diff --git a/base/common/python/pki/cryptoutil.py b/base/common/python/pki/cryptoutil.py
new file mode 100644
index 000000000..e4a01e323
--- /dev/null
+++ b/base/common/python/pki/cryptoutil.py
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+# Authors:
+# Ade Lee <alee@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Red Hat, Inc.
+# All rights reserved.
+#
+'''
+Module containing crypto classes.
+'''
+import abc
+import base64
+import nss.nss as nss
+
+
+class CryptoUtil(object):
+ '''
+ Abstract class containing methods to do cryptographic operations.
+ '''
+ __metaclass__ = abc.ABCMeta
+
+ def __init__(self):
+ ''' Constructor '''
+ pass
+
+ @abc.abstractmethod
+ def generate_symmetric_key(self, mechanism=None):
+ ''' Generate and return a symmetric key '''
+ pass
+
+ @abc.abstractmethod
+ def symmetric_wrap(self, data, wrapping_key, mechanism=None, nonce_iv=None):
+ ''' encrypt data using a symmetric key (wrapping key)'''
+ pass
+
+ @abc.abstractmethod
+ def symmetric_unwrap(self, data, wrapping_key, mechanism=None, nonce_iv=None):
+ ''' decrypt data originally encrypted with symmetric key (wrapping key)
+
+ We expect the data and nonce_iv values to be base64 encoded.
+ The mechanism is the type of key used to do the wrapping. It defaults
+ to a 56 bit DES3 key.
+ '''
+ pass
+
+ @abc.abstractmethod
+ def asymmetric_wrap(self, data, wrapping_cert, mechanism=None):
+ ''' encrypt a symmetric key with the public key of a transport cert.
+
+ The mechanism is the type of symmetric key, which defaults to a 56 bit
+ DES3 key.
+ '''
+ pass
+
+ #abc.abstractmethod
+ def get_cert(self, cert_nick):
+ ''' Get the certificate for the specified cert_nick. '''
+ pass
+
+class NSSCryptoUtil(CryptoUtil):
+ '''
+ Class that defines NSS implementation of CryptoUtil.
+ Requires an NSS database to have been set up and initialized.
+ '''
+
+ def __init__(self, certdb_dir, certdb_password):
+ ''' Initialize nss and nss related parameters
+
+ This method expects a NSS database to have already been created at
+ certdb_dir with password certdb_password.
+ '''
+ CryptoUtil.__init__(self)
+ self.certdb_dir = certdb_dir
+ self.certdb_password = certdb_password
+ nss.nss_init(certdb_dir)
+ self.nonce_iv = "e4:bb:3b:d3:c3:71:2e:58"
+
+ @staticmethod
+ def setup_contexts(mechanism, sym_key, nonce_iv):
+ ''' Set up contexts to do wrapping/unwrapping by symmetric keys. '''
+ # Get a PK11 slot based on the cipher
+ slot = nss.get_best_slot(mechanism)
+
+ if sym_key == None:
+ sym_key = slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism))
+
+ # If initialization vector was supplied use it, otherwise set it to None
+ if nonce_iv:
+ iv_data = nss.read_hex(nonce_iv)
+ iv_si = nss.SecItem(iv_data)
+ iv_param = nss.param_from_iv(mechanism, iv_si)
+ else:
+ iv_length = nss.get_iv_length(mechanism)
+ if iv_length > 0:
+ iv_data = nss.generate_random(iv_length)
+ iv_si = nss.SecItem(iv_data)
+ iv_param = nss.param_from_iv(mechanism, iv_si)
+ else:
+ iv_param = None
+
+ # Create an encoding context
+ encoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_ENCRYPT,
+ sym_key, iv_param)
+
+ # Create a decoding context
+ decoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_DECRYPT,
+ sym_key, iv_param)
+
+ return encoding_ctx, decoding_ctx
+
+ def generate_symmetric_key(self, mechanism=nss.CKM_DES3_CBC_PAD):
+ ''' Returns a symmetric key.'''
+ slot = nss.get_best_slot(mechanism)
+ return slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism))
+
+ def symmetric_wrap(self, data, wrapping_key, mechanism=nss.CKM_DES3_CBC_PAD, nonce_iv=None):
+ '''
+ :param data: Data to be wrapped
+ :param wrapping_key Symmetric key to wrap data
+
+ Wrap (encrypt) data using the supplied symmetric key
+ '''
+ encoding_ctx, _decoding_ctx = self.setup_contexts(mechanism, wrapping_key, nonce_iv)
+ wrapped_data = encoding_ctx.cipher_op(data) + encoding_ctx.digest_final()
+ return wrapped_data
+
+ def symmetric_unwrap(self, data, wrapping_key, mechanism=nss.CKM_DES3_CBC_PAD, nonce_iv=None):
+ '''
+ :param data: Data to be unwrapped (base 64 encoded)
+ :param wrapping_key Symmetric key to unwrap data
+ :param nonce_iv Base 64 encoded iv data
+
+ Unwrap (decrypt) data using the supplied symmetric key
+ '''
+ if nonce_iv == None:
+ nonce_iv = self.nonce_iv
+ else:
+ nonce_iv = nss.data_to_hex(base64.decodestring(nonce_iv))
+
+ _encoding_ctx, decoding_ctx = self.setup_contexts(mechanism, wrapping_key, nonce_iv)
+ unwrapped_data = decoding_ctx.cipher_op(base64.decodestring(data)) \
+ + decoding_ctx.digest_final()
+ return unwrapped_data
+
+ def asymmetric_wrap(self, data, wrapping_cert, mechanism=nss.CKM_DES3_CBC_PAD):
+ '''
+ :param data: Data to be wrapped
+ :param wrapping_cert Public key to wrap data
+ :param mechanism algorithm of symmetric key to be wrapped
+
+ Wrap (encrypt) data using the supplied asymmetric key
+ '''
+ public_key = wrapping_cert.subject_public_key_info.public_key
+ return base64.b64encode(nss.pub_wrap_sym_key(mechanism, public_key, data))
+
+ def get_cert(self, cert_nick):
+ '''
+ :param cert_nick Nickname for the certificate to be returned
+
+ Searches NSS database and returns SecItem object for this certificate.
+ '''
+ return nss.find_cert_from_nickname(cert_nick)
diff --git a/base/common/python/pki/key.py b/base/common/python/pki/key.py
index a55696cb3..0572ea264 100644
--- a/base/common/python/pki/key.py
+++ b/base/common/python/pki/key.py
@@ -20,10 +20,10 @@
# All rights reserved.
#
'''
-Module containing the Python client classes for the KeyResource and
-KeyRequestResource REST API on a DRM
+Module containing the Python client classes for the KeyClient and
+KeyRequestClient REST API on a DRM
'''
-import pki.encoder as e
+import pki.encoder as encoder
import json
import types
@@ -57,11 +57,13 @@ class KeyData(object):
self.size = None
self.wrappedPrivateData = None
- def from_dict(self, attr_list):
+ @classmethod
+ def from_dict(cls, attr_list):
''' Return a KeyData object from a JSON dict '''
+ key_data = cls()
for key in attr_list:
- setattr(self, key, attr_list[key])
- return self
+ setattr(key_data, key, attr_list[key])
+ return key_data
class KeyInfo(object):
'''
@@ -79,11 +81,13 @@ class KeyInfo(object):
self.ownerName = None
self.size = None
- def from_dict(self, attr_list):
+ @classmethod
+ def from_dict(cls, attr_list):
''' Return KeyInfo from JSON dict '''
+ key_info = cls()
for key in attr_list:
- setattr(self, key, attr_list[key])
- return self
+ setattr(key_info, key, attr_list[key])
+ return key_info
def get_key_id(self):
''' Return the key ID as parsed from key URL '''
@@ -104,17 +108,17 @@ class KeyInfoCollection(object):
self.key_infos = []
self.links = []
- def decode_from_json(self, json_value):
- ''' Populate the object from its JSON representation '''
+ @classmethod
+ def from_json(cls, json_value):
+ ''' Return a KeyInfoCollection object from its JSON representation '''
+ ret = cls()
infos = json_value['entries']
if not isinstance(infos, types.ListType):
- info = KeyInfo()
- self.key_infos.append(info.from_dict(infos))
+ ret.key_infos.append(KeyInfo.from_dict(infos))
else:
for info in infos:
- key_info = KeyInfo()
- key_info.from_dict(info)
- self.key_infos.append(key_info)
+ ret.key_infos.append(KeyInfo.from_dict(info))
+ return ret
class KeyRequestInfo(object):
'''
@@ -129,11 +133,13 @@ class KeyRequestInfo(object):
self.keyURL = None
self.requestStatus = None
- def from_dict(self, attr_list):
+ @classmethod
+ def from_dict(cls, attr_list):
''' Return a KeyRequestInfo object from a JSON dict. '''
+ key_request_info = cls()
for key in attr_list:
- setattr(self, key, attr_list[key])
- return self
+ setattr(key_request_info, key, attr_list[key])
+ return key_request_info
def get_request_id(self):
''' Return the request ID by parsing the request URL. '''
@@ -161,17 +167,17 @@ class KeyRequestInfoCollection(object):
self.key_requests = []
self.links = []
- def decode_from_json(self, json_value):
- ''' Populate the object from its JSON representation. '''
+ @classmethod
+ def from_json(cls, json_value):
+ ''' Return a KeyRequestInfoCollection object from its JSON representation. '''
+ ret = cls()
infos = json_value['entries']
if not isinstance(infos, types.ListType):
- info = KeyRequestInfo()
- self.key_requests.append(info.from_dict(infos))
+ ret.key_requests.append(KeyRequestInfo.from_dict(infos))
else:
for info in infos:
- key_request_info = KeyRequestInfo()
- key_request_info.from_dict(info)
- self.key_requests.append(key_request_info)
+ ret.key_requests.append(KeyRequestInfo.from_dict(info))
+ return ret
class KeyRequestResponse(object):
'''
@@ -186,10 +192,26 @@ class KeyRequestResponse(object):
self.requestInfo = None
self.keyData = None
- def decode_from_json(self, json_value):
- ''' Populate the object from its JSON representation. '''
- self.requestInfo = KeyRequestInfo()
- self.requestInfo.from_dict(json_value['RequestInfo'])
+ @classmethod
+ def from_json(cls, json_value):
+ ''' Return a KeyRequestResponse object from its JSON representation. '''
+ ret = cls()
+
+ if 'RequestInfo' in json_value:
+ ret.requestInfo = KeyRequestInfo.from_dict(json_value['RequestInfo'])
+
+ if 'KeyData' in json_value:
+ ret.keyData = KeyData.from_dict(json_value['KeyData'])
+ return ret
+
+ def get_key_id(self):
+ ''' Return the id for the key archived, recovered or generated '''
+ return self.requestInfo.get_key_id()
+
+ def get_request_id(self):
+ ''' Return the id for the created request '''
+ return self.requestInfo.get_request_id()
+
class Attribute(object):
'''
@@ -290,27 +312,33 @@ class SymKeyGenerationRequest(ResourceMessage):
ENCRYPT_USAGE = "encrypt"
def __init__(self, client_id=None, key_size=None, key_algorithm=None,
- key_usage=None):
+ key_usages=None):
''' Constructor '''
ResourceMessage.__init__(self,
"com.netscape.certsrv.key.SymKeyGenerationRequest")
+ key_usages = key_usages or []
self.add_attribute("clientID", client_id)
self.add_attribute("keySize", key_size)
self.add_attribute("keyAlgorithm", key_algorithm)
- self.add_attribute("keyUsage", key_usage)
+ self.add_attribute("keyUsage", ','.join(key_usages))
-class KeyResource(object):
+class KeyClient(object):
'''
Class that encapsulates and mirrors the functions in the KeyResource
- Java class in the DRM REST API.
+ and KeyRequestResource Java classes in the DRM REST API.
'''
+ SYMMETRIC_KEY_TYPE = "symmetricKey"
+ PASS_PHRASE_TYPE = "passPhrase"
+ ASYMMETRIC_KEY_TYPE = "asymmetricKey"
+
def __init__(self, connection):
''' Constructor '''
self.connection = connection
self.headers = {'Content-type': 'application/json',
'Accept': 'application/json'}
self.keyURL = '/rest/agent/keys'
+ self.keyRequestsURL = '/rest/agent/keyrequests'
def list_keys(self, client_id=None, status=None, max_results=None,
max_time=None, start=None, size=None):
@@ -323,9 +351,7 @@ class KeyResource(object):
'maxResults':max_results, 'maxTime':max_time,
'start':start, 'size':size}
response = self.connection.get(self.keyURL, self.headers, params=query_params)
- kdis = KeyInfoCollection()
- kdis.decode_from_json(response.json())
- return kdis
+ return KeyInfoCollection.from_json(response.json())
def retrieve_key(self, data):
''' Retrieve a secret from the DRM.
@@ -338,29 +364,41 @@ class KeyResource(object):
Returns a KeyData object containing the wrapped secret.
'''
url = self.keyURL + '/retrieve'
- print url
- e.NOTYPES['KeyRecoveryRequest'] = KeyRecoveryRequest
- e.NOTYPES['ResourceMessage'] = ResourceMessage
- e.NOTYPES['Attribute'] = Attribute
- e.NOTYPES['AttributeList'] = AttributeList
- keyRequest = json.dumps(data, cls=e.CustomTypeEncoder, sort_keys=True)
+ keyRequest = json.dumps(data, cls=encoder.CustomTypeEncoder, sort_keys=True)
response = self.connection.post(url, keyRequest, self.headers)
- keydata = KeyData()
- keydata.from_dict(response.json())
- return keydata
+ return KeyData.from_dict(response.json())
-class KeyRequestResource(object):
- '''
- Class that encapsulates and mirrors the functions in the KeyRequestResource
- Java class in the DRM REST API/
- '''
+ def request_key_retrieval(self, key_id, request_id, trans_wrapped_session_key=None,
+ session_wrapped_passphrase=None, passphrase=None, nonce_data=None):
+ ''' Retrieve a secret from the DRM.
- def __init__(self, connection):
- ''' Constructor '''
- self.connection = connection
- self.headers = {'Content-type': 'application/json',
- 'Accept': 'application/json'}
- self.keyRequestsURL = '/rest/agent/keyrequests'
+ The secret (which is referenced by key_id) can be retrieved only if the
+ recovery request (referenced by request_id) is approved. key_id and request_id
+ are required.
+
+ Data must be provided to wrap the recovered secret. This can either be
+ a) a 56-bit DES3 symmetric key, wrapped by the DRM transport key, and
+ passed in trans_wrapped_session_key
+ b) a passphrase. In this case, the passphrase must be wrapped by a 56-bit
+ symmetric key ("the session key" and passed in session_wrapped_passphrase,
+ and the session key must be wrapped by the DRM transport key and passed
+ in trans_wrapped_session_key
+ c) a passphrase for a p12 file. If the key being recovered is an asymmetric
+ key, then it is possible to pass in the passphrase for the P12 file to
+ be generated. This is passed in as passphrase
+
+ nonce_data may also be passed as a salt.
+
+ Returns a KeyData object containing the wrapped secret.
+ '''
+ request = KeyRecoveryRequest(key_id=key_id,
+ request_id=request_id,
+ trans_wrapped_session_key=trans_wrapped_session_key,
+ session_wrapped_passphrase=session_wrapped_passphrase,
+ nonce_data=nonce_data,
+ passphrase=passphrase)
+
+ return self.retrieve_key(request)
def list_requests(self, request_state=None, request_type=None, client_id=None,
start=None, page_size=None, max_results=None, max_time=None):
@@ -374,17 +412,13 @@ class KeyRequestResource(object):
'maxResults':max_results, 'maxTime':max_time}
response = self.connection.get(self.keyRequestsURL, self.headers,
params=query_params)
- kdis = KeyRequestInfoCollection()
- kdis.decode_from_json(response.json())
- return kdis
+ return KeyRequestInfoCollection.from_json(response.json())
def get_request_info(self, request_id):
''' Return a KeyRequestInfo object for a specific request. '''
- url = self.keyRequestsURL + '/' + request_id.value
+ url = self.keyRequestsURL + '/' + request_id
response = self.connection.get(url, self.headers)
- info = KeyRequestInfo()
- info.from_dict(response.json())
- return info
+ return KeyRequestInfo.from_dict(response.json())
def create_request(self, request):
''' Submit an archival, recovery or key generation request
@@ -396,63 +430,92 @@ class KeyRequestResource(object):
returns a KeyRequestResponse object.
'''
url = self.keyRequestsURL
- print request
- e.NOTYPES['SymKeyGenerationRequest'] = SymKeyGenerationRequest
- e.NOTYPES['KeyArchivalRequest'] = KeyArchivalRequest
- e.NOTYPES['KeyRecoveryRequest'] = KeyRecoveryRequest
- e.NOTYPES['ResourceMessage'] = ResourceMessage
- e.NOTYPES['Attribute'] = Attribute
- e.NOTYPES['AttributeList'] = AttributeList
- key_request1 = json.dumps(request, cls=e.CustomTypeEncoder, sort_keys=True)
- print key_request1
- response = self.connection.post(url, key_request1, self.headers)
- key_response = KeyRequestResponse()
- key_response.decode_from_json(response.json())
- return key_response
+ key_request = json.dumps(request, cls=encoder.CustomTypeEncoder, sort_keys=True)
+ response = self.connection.post(url, key_request, self.headers)
+ return KeyRequestResponse.from_json(response.json())
def approve_request(self, request_id):
''' Approve a secret recovery request '''
- url = self.keyRequestsURL + '/' + request_id.value + '/approve'
+ url = self.keyRequestsURL + '/' + request_id + '/approve'
return self.connection.post(url, self.headers)
def reject_request(self, request_id):
''' Reject a secret recovery request. '''
- url = self.keyRequestsURL + '/' + request_id.value + '/reject'
+ url = self.keyRequestsURL + '/' + request_id + '/reject'
return self.connection.post(url, self.headers)
def cancel_request(self, request_id):
''' Cancel a secret recovery request '''
- url = self.keyRequestsURL + '/' + request_id.value + '/cancel'
+ url = self.keyRequestsURL + '/' + request_id + '/cancel'
return self.connection.post(url, self.headers)
+ def request_recovery(self, key_id, request_id=None, session_wrapped_passphrase=None,
+ trans_wrapped_session_key=None, b64certificate=None, nonce_data=None):
+ ''' Create a request to recover a secret.
+
+ To retrieve a symmetric key or passphrase, the only parameter that is required is
+ the keyId. It is possible (but not required) to pass in the session keys/passphrase
+ and nonceData for the retrieval at this time. Those parameters are documented
+ in the docstring for retrieve_key below.
+
+ To retrieve an asymmetric key, the keyId and the the base-64 encoded certificate
+ is required.
+ '''
+ request = KeyRecoveryRequest(key_id=key_id,
+ request_id=request_id,
+ trans_wrapped_session_key=trans_wrapped_session_key,
+ session_wrapped_passphrase=session_wrapped_passphrase,
+ certificate=b64certificate,
+ nonce_data=nonce_data)
+ return self.create_request(request)
+
+ def request_archival(self, client_id, data_type, wrapped_private_data,
+ key_algorithm=None, key_size=None):
+ ''' Archive a secret (symmetric key or passphrase) on the DRM.
+
+ Requires a user-supplied client ID. There can be only one active
+ key with a specified client ID. If a record for a duplicate active
+ key exists, an exception is thrown.
+
+ data_type can be one of the following:
+
+ wrapped_private_data consists of a PKIArchiveOptions structure, which
+ can be constructed using either generate_archive_options() or
+ generate_pki_archive_options() below.
+
+ key_algorithm and key_size are applicable to symmetric keys only.
+ If a symmetric key is being archived, these parameters are required.
+ '''
+ request = KeyArchivalRequest(client_id=client_id,
+ data_type=data_type,
+ wrapped_private_data=wrapped_private_data,
+ key_algorithm=key_algorithm,
+ key_size=key_size)
+ return self.create_request(request)
+
+encoder.NOTYPES['Attribute'] = Attribute
+encoder.NOTYPES['AttributeList'] = AttributeList
+encoder.NOTYPES['KeyArchivalRequest'] = KeyArchivalRequest
+encoder.NOTYPES['KeyRecoveryRequest'] = KeyRecoveryRequest
+encoder.NOTYPES['ResourceMessage'] = ResourceMessage
+encoder.NOTYPES['SymKeyGenerationRequest'] = SymKeyGenerationRequest
def main():
+ ''' Some unit tests - basically printing different types of requests '''
print "printing symkey generation request"
client_id = "vek 123"
gen_request = SymKeyGenerationRequest(client_id, 128, "AES", "encrypt,decrypt")
- e.NOTYPES['SymKeyGenerationRequest'] = SymKeyGenerationRequest
- e.NOTYPES['ResourceMessage'] = ResourceMessage
- e.NOTYPES['Attribute'] = Attribute
- e.NOTYPES['AttributeList'] = AttributeList
- print json.dumps(gen_request, cls=e.CustomTypeEncoder, sort_keys=True)
+ print json.dumps(gen_request, cls=encoder.CustomTypeEncoder, sort_keys=True)
print "printing key recovery request"
key_request = KeyRecoveryRequest("25", "MX12345BBBAAA", None,
"1234ABC", None, None)
- e.NOTYPES['KeyRecoveryRequest'] = KeyRecoveryRequest
- e.NOTYPES['ResourceMessage'] = ResourceMessage
- e.NOTYPES['Attribute'] = Attribute
- e.NOTYPES['AttributeList'] = AttributeList
- print json.dumps(key_request, cls=e.CustomTypeEncoder, sort_keys=True)
+ print json.dumps(key_request, cls=encoder.CustomTypeEncoder, sort_keys=True)
print "printing key archival request"
archival_request = KeyArchivalRequest(client_id, "symmetricKey",
"MX123AABBCD", "AES", 128)
- e.NOTYPES['KeyArchivalRequest'] = KeyArchivalRequest
- e.NOTYPES['ResourceMessage'] = ResourceMessage
- e.NOTYPES['Attribute'] = Attribute
- e.NOTYPES['AttributeList'] = AttributeList
- print json.dumps(archival_request, cls=e.CustomTypeEncoder, sort_keys=True)
+ print json.dumps(archival_request, cls=encoder.CustomTypeEncoder, sort_keys=True)
if __name__ == '__main__':
main()
diff --git a/base/common/python/pki/kraclient.py b/base/common/python/pki/kraclient.py
index 0cb6707ef..f2b7a5582 100644
--- a/base/common/python/pki/kraclient.py
+++ b/base/common/python/pki/kraclient.py
@@ -21,141 +21,144 @@
#
'''
Module containing KRAClient class. This class should be used by Python clients
-to interact with the DRM to expose the functionality of the KeyResource and
+to interact with the DRM to expose the functionality of the KeyClient and
KeyRequestResouce REST APIs.
'''
-import base64
-import pki.client as client
import pki.key as key
-import pki.cert as cert
-import nss.nss as nss
-import time
+
+from pki.systemcert import SystemCertClient
class KRAClient(object):
'''
Client class that models interactions with a KRA using the Key and KeyRequest REST APIs.
'''
- def __init__(self, connection):
- ''' Constructor '''
+ def __init__(self, connection, crypto, transport_cert_nick):
+ ''' Constructor
+
+ :param connection - PKIConnection object with DRM connection info.
+ :param crypto - CryptoUtil object. NSSCryptoUtil is provided by default.
+ If a different crypto implementation is desired, a different
+ subclass of CryptoUtil must be provided.
+ :param trnasport_cert_nick - identifier for the DRM transport certificate. This will
+ be passed to the CryptoUtil.get_cert() command to get a representation
+ of the transport certificate usable for crypto operations.
+ '''
self.connection = connection
- self.key_resource = key.KeyResource(connection)
- self.key_request_resource = key.KeyRequestResource(connection)
- self.cert_resource = cert.CertResource(connection)
+ self.keys = key.KeyClient(connection)
+ self.system_certs = SystemCertClient(connection)
+ self.crypto = crypto
+ self.transport_cert = crypto.get_cert(transport_cert_nick)
- # nss parameters
- self.certdb_dir = None
- self.certdb_password = None
- self.transport_nick = None
- self.transport_cert = None
+ def retrieve_key(self, key_id, trans_wrapped_session_key=None):
+ ''' Retrieve a secret (passphrase or symmetric key) from the DRM.
- def initialize_nss(self, certdb_dir, certdb_password, transport_nick):
- ''' Initialize nss and nss related parameters
+ This function generates a key recovery request, approves it, and retrieves
+ the secret referred to by key_id. This assumes that only one approval is required
+ to authorize the recovery.
- We expect this method to be called when an nss database is to
- be used to do client side cryptographic operations.
+ To ensure data security in transit, the data will be returned encrypted by a session
+ key (56 bit DES3 symmetric key) - which is first wrapped (encrypted) by the public
+ key of the DRM transport certificate before being sent to the DRM. The
+ parameter trans_wrapped_session_key refers to this wrapped session key.
- This method expects a NSS database to have already been created at
- certdb_dir with password certdb_password, and the DRM transport
- certificate to have been imported as transport_nick
- '''
- self.certdb_dir = certdb_dir
- self.certdb_password = certdb_password
- self.transport_nick = transport_nick
- nss.nss_init(certdb_dir)
- self.transport_cert = nss.find_cert_from_nickname(self.transport_nick)
-
- def get_transport_cert(self):
- ''' Return the b64 of the transport certificate. '''
- return self.cert_resource.get_transport_cert()
-
- def list_requests(self, request_state, request_type, start=0,
- page_size=100, max_results=100, max_time=10):
- ''' Search for a list of key requests of a specified type and state.
-
- The permitted values for request_state are:XXXX
- The permitted values for request_type are:
-
- Return a list of KeyRequestInfo objects '''
- return self.key_request_resource.list_requests(request_state=request_state,
- request_type=request_type,
- start=start,
- page_size=page_size,
- max_results=max_results,
- max_time=max_time)
- def get_request(self, request_id):
- ''' Return a KeyRequestInfo object for a specific request '''
- return self.key_request_resource.get_request_info(key.RequestId(request_id))
-
- def list_keys(self, client_id, status):
- ''' Search for secrets archived in the DRM with a given client ID and status.
-
- The permitted values for status are: active, inactive
- Return a list of KeyInfo objects
+ There are two ways of using this function:
+
+ 1) trans_wrapped_session_key is not provided by caller.
+
+ In this case, the function will call CryptoUtil methods to generate and wrap the
+ session key. The function will return the tuple (KeyData, unwrapped_secret)
+
+ 2) The trans_wrapped_session_key is provided by the caller.
+
+ In this case, the function will simply pass the data to the DRM, and will return the secret
+ wrapped in the session key. The secret will still need to be unwrapped by the caller.
+
+ The function will return the tuple (KeyData, None), where the KeyData structure includes the
+ wrapped secret and some nonce data to be used as a salt when unwrapping.
'''
- return self.key_resource.list_keys(client_id, status)
+ key_provided = True
+ if (trans_wrapped_session_key == None):
+ key_provided = False
+ session_key = self.crypto.generate_symmetric_key()
+ trans_wrapped_session_key = self.crypto.asymmetric_wrap(session_key,
+ self.transport_cert)
+
+ response = self.keys.request_recovery(key_id)
+ request_id = response.get_request_id()
+ self.keys.approve_request(request_id)
+
+ key_data = self.keys.request_key_retrieval(key_id, request_id,
+ trans_wrapped_session_key=trans_wrapped_session_key)
+ if key_provided:
+ return key_data, None
+
+ unwrapped_key = self.crypto.symmetric_unwrap(key_data.wrappedPrivateData, session_key,
+ iv=key_data.nonceData)
+ return key_data, unwrapped_key
+
+ def retrieve_key_by_passphrase(self, key_id, passphrase=None,
+ trans_wrapped_session_key=None,
+ session_wrapped_passphrase=None,
+ nonce_data=None):
+ ''' Retrieve a secret (passphrase or symmetric key) from the DRM using a passphrase.
+
+ This function generates a key recovery request, approves it, and retrieves
+ the secret referred to by key_id. This assumes that only one approval is required
+ to authorize the recovery.
+
+ The secret is secured in transit by wrapping the secret with a passphrase using
+ PBE encryption.
+
+ There are two ways of using this function:
+
+ 1) A passphrase is provided by the caller.
+
+ In this case, CryptoUtil methods will be called to create the data to securely send the
+ passphrase to the DRM. Basically, three pieces of data will be sent:
+
+ - the passphrase wrapped by a 56 bit DES3 symmetric key (the session key). This
+ is referred to as the parameter session_wrapped_passphrase above.
+
+ - the session key wrapped with the public key in the DRM transport certificate. This
+ is referred to as the trans_wrapped_session_key above.
- def request_recovery(self, key_id, request_id=None, session_wrapped_passphrase=None,
- trans_wrapped_session_key=None, b64certificate=None, nonce_data=None):
- ''' Create a request to recover a secret.
+ - ivps nonce data, referred to as nonce_data
- To retrieve a symmetric key or passphrase, the only parameter that is required is
- the keyId. It is possible (but not required) to pass in the session keys/passphrase
- and nonceData for the retrieval at this time. Those parameters are documented
- in the docstring for retrieve_key below.
+ The function will return the tuple (KeyData, unwrapped_secret)
- To retrieve an asymmetric key, the keyId and the the base-64 encoded certificate
- is required.
+ 2) The caller provides the trans_wrapped_session_key, session_wrapped_passphrase
+ and nonce_data.
+
+ In this case, the data will simply be passed to the DRM. The function will return
+ the secret encrypted by the passphrase using PBE Encryption. The secret will still
+ need to be decrypted by the caller.
+
+ The function will return the tuple (KeyData, None)
'''
- request = key.KeyRecoveryRequest(key_id=key_id,
- request_id=request_id,
- trans_wrapped_session_key=trans_wrapped_session_key,
- session_wrapped_passphrase=session_wrapped_passphrase,
- certificate=b64certificate,
- nonce_data=nonce_data)
- return self.key_request_resource.create_request(request)
-
- def approve_request(self, request_id):
- ''' Approve a key recovery request '''
- return self.key_request_resource.approve_request(key.RequestId(request_id))
-
- def reject_request(self, request_id):
- ''' Reject a key recovery request '''
- return self.key_request_resource.reject_request(key.RequestId(request_id))
-
- def cancel_request(self, request_id):
- ''' Cancel a key recovery request '''
- return self.key_request_resource.cancel_request(key.RequestId(request_id))
-
- def retrieve_key(self, key_id, request_id, trans_wrapped_session_key=None,
- session_wrapped_passphrase=None, passphrase=None, nonce_data=None):
- ''' Retrieve a secret from the DRM.
-
- The secret (which is referenced by key_id) can be retrieved only if the
- recovery request (referenced by request_id) is approved. key_id and request_id
- are required.
-
- Data must be provided to wrap the recovered secret. This can either be
- a) a 56-bit DES3 symmetric key, wrapped by the DRM transport key, and
- passed in trans_wrapped_session_key
- b) a passphrase. In this case, the passphrase must be wrapped by a 56-bit
- symmetric key ("the session key" and passed in session_wrapped_passphrase,
- and the session key must be wrapped by the DRM transport key and passed
- in trans_wrapped_session_key
- c) a passphrase for a p12 file. If the key being recovered is an asymmetric
- key, then it is possible to pass in the passphrase for the P12 file to
- be generated. This is passed in as passphrase
-
- nonce_data may also be passed as a salt.
+ pass
+
+ def retrieve_key_by_pkcs12(self, key_id, certificate, passphrase):
+ ''' Retrieve an asymmetric private key and return it as PKCS12 data.
+
+ This function generates a key recovery request, approves it, and retrieves
+ the secret referred to by key_id in a PKCS12 file. This assumes that only
+ one approval is required to authorize the recovery.
+
+ This function requires the following parameters:
+ - key_id : the ID of the key
+ - certificate: the certificate associated with the private key
+ - passphrase: A passphrase for the pkcs12 file.
+
+ The function returns a KeyData object.
'''
- request = key.KeyRecoveryRequest(key_id=key_id,
- request_id=request_id,
- trans_wrapped_session_key=trans_wrapped_session_key,
- session_wrapped_passphrase=session_wrapped_passphrase,
- nonce_data=nonce_data,
- passphrase=passphrase)
- return self.key_resource.retrieve_key(request)
+ response = self.keys.request_recovery(key_id, b64certificate=certificate)
+ request_id = response.get_request_id()
+ self.keys.approve_request(request_id)
+
+ return self.keys.request_key_retrieval(key_id, request_id, passphrase)
+
def generate_sym_key(self, client_id, algorithm, size, usages):
''' Generate and archive a symmetric key on the DRM.
@@ -166,32 +169,47 @@ class KRAClient(object):
request = key.SymKeyGenerationRequest(client_id=client_id,
key_size=size,
key_algorithm=algorithm,
- key_usage=usages)
- return self.key_request_resource.create_request(request)
+ key_usages=usages)
+ return self.keys.create_request(request)
- def archive_key(self, client_id, data_type, wrapped_private_data,
+ def archive_key(self, client_id, data_type, private_data=None,
+ wrapped_private_data=None,
key_algorithm=None, key_size=None):
- ''' Archive a secret (symetric key or passphrase) on the DRM.
+ ''' Archive a secret (symmetric key or passphrase) on the DRM.
Requires a user-supplied client ID. There can be only one active
key with a specified client ID. If a record for a duplicate active
- key exists, an exception is thrown.
+ key exists, a BadRequestException is thrown.
data_type can be one of the following:
+ KeyRequestResource.SYMMETRIC_KEY_TYPE,
+ KeyRequestResource.ASYMMETRIC_KEY_TYPE,
+ KeyRequestResource.PASS_PHRASE_TYPE
+
+ key_algorithm and key_size are applicable to symmetric keys only.
+ If a symmetric key is being archived, these parameters are required.
wrapped_private_data consists of a PKIArchiveOptions structure, which
can be constructed using either generate_archive_options() or
generate_pki_archive_options() below.
- key_algorithm and key_size are applicable to symmetric keys only.
- If a symmetric key is being archived, these parameters are required.
+ private_data is the secret that is to be archived.
+
+ Callers must specify EITHER wrapped_private_data OR private_data.
+ If wrapped_private_data is specified, then this data is forwarded to the
+ DRM unchanged. Otherwise, the private_data is converted to a
+ PKIArchiveOptions structure using the functions below.
+
+ The function returns a KeyRequestResponse object containing a KeyRequestInfo
+ object with details about the archival request and key archived.
'''
- request = key.KeyArchivalRequest(client_id=client_id,
- data_type=data_type,
- wrapped_private_data=wrapped_private_data,
- key_algorithm=key_algorithm,
- key_size=key_size)
- return self.key_request_resource.create_request(request)
+ if wrapped_private_data == None:
+ if private_data == None:
+ # raise BadRequestException - to be added in next patch
+ return None
+ wrapped_private_data = self.generate_archive_options(private_data)
+ return self.keys.request_archival(client_id, data_type, wrapped_private_data,
+ key_algorithm, key_size)
def generate_pki_archive_options(self, trans_wrapped_session_key, session_wrapped_secret):
''' Return a PKIArchiveOptions structure for archiving a secret
@@ -202,38 +220,6 @@ class KRAClient(object):
'''
pass
- def setup_contexts(self, mechanism, sym_key, iv_vector):
- ''' Set up contexts to do wrapping/unwrapping by symmetric keys. '''
- # Get a PK11 slot based on the cipher
- slot = nss.get_best_slot(mechanism)
-
- if sym_key == None:
- sym_key = slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism))
-
- # If initialization vector was supplied use it, otherwise set it to None
- if iv_vector:
- iv_data = nss.read_hex(iv_vector)
- iv_si = nss.SecItem(iv_data)
- iv_param = nss.param_from_iv(mechanism, iv_si)
- else:
- iv_length = nss.get_iv_length(mechanism)
- if iv_length > 0:
- iv_data = nss.generate_random(iv_length)
- iv_si = nss.SecItem(iv_data)
- iv_param = nss.param_from_iv(mechanism, iv_si)
- else:
- iv_param = None
-
- # Create an encoding context
- encoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_ENCRYPT,
- sym_key, iv_param)
-
- # Create a decoding context
- decoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_DECRYPT,
- sym_key, iv_param)
-
- return encoding_ctx, decoding_ctx
-
def generate_archive_options(self, secret):
''' Return a PKIArchiveOptions structure for archiving a secret.
@@ -246,141 +232,9 @@ class KRAClient(object):
This method expects initialize_nss() to have been called previously.
'''
- mechanism = nss.CKM_DES3_CBC_PAD
- slot = nss.get_best_slot(mechanism)
- session_key = slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism))
-
- public_key = self.transport_cert.subject_public_key_info.public_key
- trans_wrapped_session_key = base64.b64encode(nss.pub_wrap_sym_key(
- mechanism, public_key, session_key))
-
- encoding_ctx, _decoding_ctx = self.setup_contexts(mechanism, session_key, None)
- wrapped_secret = encoding_ctx.cipher_op(secret) + encoding_ctx.digest_final()
+ session_key = self.crypto.generate_symmetric_key()
+ trans_wrapped_session_key = self.crypto.asymmetric_wrap(session_key, self.transport_cert)
+ wrapped_secret = self.crypto.symmetric_wrap(secret, session_key)
return self.generate_pki_archive_options(trans_wrapped_session_key, wrapped_secret)
-def print_key_request(request):
- ''' Prints the relevant fields of a KeyRequestInfo object '''
- print "RequestURL: " + str(request.requestURL)
- print "RequestType: " + str(request.requestType)
- print "RequestStatus: " + str(request.requestStatus)
- print "KeyURL: " + str(request.keyURL)
-
-def print_key_info(key_info):
- ''' Prints the relevant fields of a KeyInfo object '''
- print "Key URL: " + str(key_info.keyURL)
- print "Client ID: " + str(key_info.clientID)
- print "Algorithm: " + str(key_info.algorithm)
- print "Status: " + str(key_info.status)
- print "Owner Name: " + str(key_info.ownerName)
- print "Size: " + str(key_info.size)
-
-def print_key_data(key_data):
- ''' Prints the relevant fields of a KeyData object '''
- print "Key Algorithm: " + str(key_data.algorithm)
- print "Key Size: " + str(key_data.size)
- print "Nonce Data: " + str(key_data.nonceData)
- print "Wrapped Private Data: " + str(key_data.wrappedPrivateData)
-
-def generate_symmetric_key(mechanism):
- ''' generate symmetric key - to be moved to nssutil module'''
- slot = nss.get_best_slot(mechanism)
- return slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism))
-
-def trans_wrap_sym_key(transport_cert, sym_key, mechanism):
- ''' wrap a sym key with a transport cert - to be moved to nsutil module'''
- public_key = transport_cert.subject_public_key_info.public_key
- return base64.b64encode(nss.pub_wrap_sym_key(mechanism, public_key, sym_key))
-
-def barbican_encode(kraclient, client_id, algorithm, key_size, usage_string):
- response = kraclient.generate_sym_key(client_id, algorithm, key_size, usage_string)
- return response.requestInfo.get_key_id()
-
-def barbican_decode(kraclient, key_id, wrapped_session_key):
- response = kraclient.request_recovery(key_id)
- recovery_request_id = response.requestInfo.get_request_id()
- kraclient.approve_request(recovery_request_id)
- return kraclient.retrieve_key(key_id, recovery_request_id, wrapped_session_key)
-
-def main():
- ''' test code execution '''
- connection = client.PKIConnection('https', 'localhost', '8443', 'kra')
- connection.set_authentication_cert('/tmp/temp4.pem')
- kraclient = KRAClient(connection)
- # Get Transport Cert
- transport_cert = kraclient.get_transport_cert()
- print transport_cert
-
- print "Now getting key request"
- keyrequest = kraclient.get_request('2')
- print_key_request(keyrequest)
-
- print "Now listing requests"
- keyrequests = kraclient.list_requests('complete', 'securityDataRecovery')
- print keyrequests.key_requests
- for request in keyrequests.key_requests:
- print_key_request(request)
-
- print "Now generating symkey"
- client_id = "Vek #1" + time.strftime('%X %x %Z')
- algorithm = "AES"
- key_size = 128
- usages = [key.SymKeyGenerationRequest.DECRYPT_USAGE, key.SymKeyGenerationRequest.ENCRYPT_USAGE]
- response = kraclient.generate_sym_key(client_id, algorithm, key_size, ','.join(usages))
- print_key_request(response.requestInfo)
- print "Request ID is " + response.requestInfo.get_request_id()
- key_id = response.requestInfo.get_key_id()
-
- print "Now getting key ID for clientID=\"" + client_id + "\""
- key_infos = kraclient.list_keys(client_id, "active")
- for key_info in key_infos.key_infos:
- print_key_info(key_info)
- key_id2 = key_info.get_key_id()
- if key_id == key_id2:
- print "Success! The keys from generation and search match."
- else:
- print "Failure - key_ids for generation do not match!"
-
- print "Submit recovery request"
- response = kraclient.request_recovery(key_id)
- print response
- print_key_request(response.requestInfo)
- recovery_request_id = response.requestInfo.get_request_id()
-
- print "Approve recovery request"
- print kraclient.approve_request(recovery_request_id)
-
- # now begins the nss specific code
- # you need to have an nss database set up with the transport cert
- # imported therein.
- print "Retrieve key"
- nss.nss_init("/tmp/drmtest/certdb")
- mechanism = nss.CKM_DES3_CBC_PAD
-
- transport_cert = nss.find_cert_from_nickname("kra transport cert")
- session_key = generate_symmetric_key(mechanism)
- print session_key
- wrapped_session_key = trans_wrap_sym_key(transport_cert, session_key, nss.CKM_DES_CBC_PAD)
-
- response = kraclient.retrieve_key(key_id, recovery_request_id, wrapped_session_key)
- print_key_data(response)
-
- # do the above again - but this time using Barbican -like encode() and decode() functions
-
- # generate a symkey
- client_id = "Barbican VEK #1" + time.strftime('%X %x %Z')
- algorithm = "AES"
- key_size = 128
- usages = [key.SymKeyGenerationRequest.DECRYPT_USAGE, key.SymKeyGenerationRequest.ENCRYPT_USAGE]
- key_id = barbican_encode(kraclient, client_id, algorithm, key_size, ','.join(usages))
- print "barbican_encode() returns " + str(key_id)
-
- # recover the symkey
- session_key = generate_symmetric_key(mechanism)
- wrapped_session_key = trans_wrap_sym_key(transport_cert, session_key, nss.CKM_DES_CBC_PAD)
- response = barbican_decode(kraclient, key_id, wrapped_session_key)
- print "barbican_decode() returns:"
- print_key_data(response)
-
-if __name__ == "__main__":
- main()
diff --git a/base/common/python/pki/systemcert.py b/base/common/python/pki/systemcert.py
new file mode 100644
index 000000000..aa1cb538d
--- /dev/null
+++ b/base/common/python/pki/systemcert.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+# Authors:
+# Ade Lee <alee@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Red Hat, Inc.
+# All rights reserved.
+#
+'''
+Module containing the Python client classes for the SystemCert REST API
+'''
+
+from pki.cert import CertData
+
+class SystemCertClient(object):
+ '''
+ Class encapsulating and mirroring the functionality in the SystemCertResouce
+ Java interface class defining the REST API for system certificate resources.
+ '''
+
+ def __init__(self, connection):
+ ''' Constructor '''
+ #super(PKIResource, self).__init__(connection)
+ self.connection = connection
+ self.headers = {'Content-type': 'application/json',
+ 'Accept': 'application/json'}
+ self.cert_url = '/rest/config/cert'
+
+ def get_transport_cert(self):
+ ''' Return transport certificate '''
+ url = self.cert_url + '/transport'
+ response = self.connection.get(url, self.headers)
+ cert_data = CertData.from_dict(response.json())
+ return cert_data.Encoded