summaryrefslogtreecommitdiffstats
path: root/base/common/python
diff options
context:
space:
mode:
authorAde Lee <alee@redhat.com>2014-02-16 15:39:37 -0500
committerAde Lee <alee@redhat.com>2014-02-19 10:48:20 -0500
commitf4aafb999efc1367c005c9683ae9d935720d2482 (patch)
tree541c8e11c06d85e1b2d36651c30992aed6ad9aea /base/common/python
parenta9c460f532a5f9697b56aa116c3df772b0fd27e9 (diff)
downloadpki-f4aafb999efc1367c005c9683ae9d935720d2482.tar.gz
pki-f4aafb999efc1367c005c9683ae9d935720d2482.tar.xz
pki-f4aafb999efc1367c005c9683ae9d935720d2482.zip
Additional changes as per review.
1. Moved most methods back into the key.py module. 2. Simplified the invocation by combining the KeyClient and KeyRequestClient as just KeyClient. 3. Added additional invocations in KRAClient - with lots of docuemntation. These are hopefully more user friendly. 4. Extracted crypto operations to cryptoutil class. This class has an NSS implementation provided. 5. Addressed other issues found in review.
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