diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/common/python/pki/key.py | 102 | ||||
-rw-r--r-- | base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java | 22 | ||||
-rw-r--r-- | base/common/src/com/netscape/certsrv/request/IRequest.java | 1 | ||||
-rw-r--r-- | base/kra/functional/drmtest.py | 26 | ||||
-rw-r--r-- | base/kra/src/CMakeLists.txt | 9 | ||||
-rw-r--r-- | base/kra/src/com/netscape/kra/SymKeyGenService.java | 4 | ||||
-rw-r--r-- | base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java | 29 |
7 files changed, 159 insertions, 34 deletions
diff --git a/base/common/python/pki/key.py b/base/common/python/pki/key.py index 7d93e783a..3fa5952db 100644 --- a/base/common/python/pki/key.py +++ b/base/common/python/pki/key.py @@ -94,7 +94,7 @@ class KeyInfo(object): def get_key_id(self): ''' Return the key ID as parsed from key URL ''' - if self.keyURL != None: + if self.keyURL is not None: indx = str(self.keyURL).rfind("/") + 1 return str(self.keyURL)[indx:] return None @@ -146,14 +146,14 @@ class KeyRequestInfo(object): def get_request_id(self): ''' Return the request ID by parsing the request URL. ''' - if self.requestURL != None: + if self.requestURL is not None: indx = str(self.requestURL).rfind("/") + 1 return str(self.requestURL)[indx:] return None def get_key_id(self): ''' Return the ID of the secret referred to by this request. ''' - if self.keyURL != None: + if self.keyURL is not None: indx = str(self.keyURL).rfind("/") + 1 return str(self.keyURL)[indx:] return None @@ -268,7 +268,7 @@ class SymKeyGenerationRequest(pki.ResourceMessage): ENCRYPT_USAGE = "encrypt" def __init__(self, client_key_id=None, key_size=None, key_algorithm=None, - key_usages=None): + key_usages=None, trans_wrapped_session_key=None): ''' Constructor ''' pki.ResourceMessage.__init__(self, "com.netscape.certsrv.key.SymKeyGenerationRequest") @@ -277,6 +277,7 @@ class SymKeyGenerationRequest(pki.ResourceMessage): self.add_attribute("keySize", key_size) self.add_attribute("keyAlgorithm", key_algorithm) self.add_attribute("keyUsage", ','.join(key_usages)) + self.add_attribute("transWrappedSessionKey", trans_wrapped_session_key) class KeyClient(object): ''' @@ -288,6 +289,9 @@ class KeyClient(object): PASS_PHRASE_TYPE = "passPhrase" ASYMMETRIC_KEY_TYPE = "asymmetricKey" + KEY_STATUS_ACTIVE = "active" + KEY_STATUS_INACTIVE = "inactive" + def __init__(self, connection, crypto, transport_cert_nick=None): ''' Constructor ''' self.connection = connection @@ -297,7 +301,7 @@ class KeyClient(object): self.keyRequestsURL = '/rest/agent/keyrequests' self.crypto = crypto - if transport_cert_nick != None: + if transport_cert_nick is not None: self.crypto.initialize() self.transport_cert = crypto.get_cert(transport_cert_nick) else: @@ -305,6 +309,8 @@ class KeyClient(object): def set_transport_cert(self, transport_cert_nick): ''' Set the transport certificate for crypto operations ''' + if transport_cert_nick is None: + raise ValueError("Transport cert nickname must be specified.") self.transport_cert = self.crypto.get_cert(transport_cert_nick) @pki.handle_exceptions() @@ -339,6 +345,9 @@ class KeyClient(object): @pki.handle_exceptions() def get_request_info(self, request_id): ''' Return a KeyRequestInfo object for a specific request. ''' + if request_id is None: + raise ValueError("request_id must be specified") + url = self.keyRequestsURL + '/' + request_id response = self.connection.get(url, self.headers) return KeyRequestInfo.from_json(response.json()) @@ -346,6 +355,9 @@ class KeyClient(object): @pki.handle_exceptions() def get_key_info(self, key_id): ''' Get the info in the KeyRecord for a specific secret in the DRM. ''' + if key_id is None: + raise ValueError("key_id must be specified") + url = self.keyURL + '/' + key_id response = self.connection.get(url, headers=self.headers) return KeyInfo.from_json(response.json()) @@ -353,6 +365,9 @@ class KeyClient(object): @pki.handle_exceptions() def get_active_key_info(self, client_key_id): ''' Get the info in the KeyRecord for the active secret in the DRM. ''' + if client_key_id is None: + raise ValueError("client_key_id must be specified") + url = self.keyURL + '/active/' + urllib.quote_plus(client_key_id) response = self.connection.get(url, headers=self.headers) return KeyInfo.from_json(response.json()) @@ -360,6 +375,9 @@ class KeyClient(object): @pki.handle_exceptions() def modify_key_status(self, key_id, status): ''' Modify the status of a key ''' + if (key_id is None) or (status is None): + raise ValueError("key_id and status must be specified") + url = self.keyURL + '/' + key_id params = {'status':status} self.connection.post(url, None, headers=self.headers, params=params) @@ -367,18 +385,27 @@ class KeyClient(object): @pki.handle_exceptions() def approve_request(self, request_id): ''' Approve a secret recovery request ''' + if request_id is None: + raise ValueError("request_id must be specified") + url = self.keyRequestsURL + '/' + request_id + '/approve' self.connection.post(url, self.headers) @pki.handle_exceptions() def reject_request(self, request_id): ''' Reject a secret recovery request. ''' + if request_id is None: + raise ValueError("request_id must be specified") + url = self.keyRequestsURL + '/' + request_id + '/reject' self.connection.post(url, self.headers) @pki.handle_exceptions() def cancel_request(self, request_id): ''' Cancel a secret recovery request ''' + if request_id is None: + raise ValueError("request_id must be specified") + url = self.keyRequestsURL + '/' + request_id + '/cancel' self.connection.post(url, self.headers) @@ -403,6 +430,9 @@ class KeyClient(object): This method expects initialize_nss() to have been called previously. ''' + if secret is None: + raise ValueError("secret must be specified") + 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) @@ -419,22 +449,42 @@ class KeyClient(object): returns a KeyRequestResponse object. ''' + if request is None: + raise ValueError("request must be specified") + url = self.keyRequestsURL 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()) @pki.handle_exceptions() - def generate_symmetric_key(self, client_key_id, algorithm, size, usages): + def generate_symmetric_key(self, client_key_id, algorithm=None, size=None, usages=None, + trans_wrapped_session_key=None): ''' Generate and archive a symmetric key on the DRM. Return a KeyRequestResponse which contains a KeyRequestInfo object that describes the URL for the request and generated key. + ''' - request = SymKeyGenerationRequest(client_key_id=client_key_id, - key_size=size, - key_algorithm=algorithm, - key_usages=usages) + if client_key_id is None: + raise ValueError("Must specify client_key_id") + + if trans_wrapped_session_key is not None: + twsk = base64.encodestring(trans_wrapped_session_key) + request = SymKeyGenerationRequest( + client_key_id=client_key_id, + key_size=size, + key_algorithm=algorithm, + key_usages=usages, + trans_wrapped_session_key=twsk) + raise NotImplementedError( + "Returning the symmetric key in the same call is not yet implemented.") + else: + request = SymKeyGenerationRequest( + client_key_id=client_key_id, + key_size=size, + key_algorithm=algorithm, + key_usages=usages) return self.create_request(request) @pki.handle_exceptions() @@ -448,9 +498,9 @@ class KeyClient(object): 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 + KeyClient.SYMMETRIC_KEY_TYPE, + KeyClient.ASYMMETRIC_KEY_TYPE, + KeyClient.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. @@ -469,8 +519,16 @@ class KeyClient(object): The function returns a KeyRequestResponse object containing a KeyRequestInfo object with details about the archival request and key archived. ''' - if wrapped_private_data == None: - if private_data == None: + if (client_key_id is None) or (data_type is None): + raise ValueError("client_key_id and data_type must be specified") + + if data_type == KeyClient.SYMMETRIC_KEY_TYPE: + if (key_algorithm is None) or (key_size is None): + raise ValueError( + "For symmetric keys, key algorithm and key_size must be specified") + + if wrapped_private_data is None: + if private_data is None: raise ValueError("No data provided to be archived") wrapped_private_data = self.generate_archive_options(private_data) @@ -494,6 +552,9 @@ class KeyClient(object): To retrieve an asymmetric key, the keyId and the the base-64 encoded certificate is required. ''' + if key_id is None: + raise ValueError("key_id must be defined") + request = KeyRecoveryRequest(key_id=key_id, request_id=request_id, trans_wrapped_session_key=trans_wrapped_session_key, @@ -513,6 +574,9 @@ class KeyClient(object): Returns a KeyData object containing the wrapped secret. ''' + if data is None: + raise ValueError("KeyRecoveryRequest must be specified") + url = self.keyURL + '/retrieve' keyRequest = json.dumps(data, cls=encoder.CustomTypeEncoder, sort_keys=True) response = self.connection.post(url, keyRequest, self.headers) @@ -546,8 +610,11 @@ class KeyClient(object): 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. ''' + if key_id is None: + raise ValueError("key_id must be specified") + key_provided = True - if (trans_wrapped_session_key == None): + if trans_wrapped_session_key is None: key_provided = False session_key = self.crypto.generate_symmetric_key() trans_wrapped_session_key = self.crypto.asymmetric_wrap(session_key, @@ -629,6 +696,9 @@ class KeyClient(object): The function returns a KeyData object. ''' + if (key_id is None) or (certificate is None) or (passphrase is None): + raise ValueError("key_id, certificate and passphrase must all be specified") + response = self.recover_key(key_id, b64certificate=certificate) request_id = response.get_request_id() self.approve_request(request_id) diff --git a/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java b/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java index 01326442f..27dc69fd5 100644 --- a/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java +++ b/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java @@ -25,6 +25,7 @@ public class SymKeyGenerationRequest extends ResourceMessage { private static final String KEY_SIZE = "keySize"; private static final String KEY_ALGORITHM = "keyAlgorithm"; private static final String KEY_USAGE = "keyUsage"; + private static final String TRANS_WRAPPED_SESSION_KEY = "transWrappedSessionKey"; /* Symmetric Key usages */ public static final String UWRAP_USAGE = "unwrap"; @@ -65,6 +66,7 @@ public class SymKeyGenerationRequest extends ResourceMessage { attributes.put(KEY_SIZE, form.getFirst(KEY_SIZE)); attributes.put(KEY_ALGORITHM, form.getFirst(KEY_ALGORITHM)); attributes.put(KEY_USAGE, form.getFirst(KEY_USAGE)); + attributes.put(TRANS_WRAPPED_SESSION_KEY, form.getFirst(TRANS_WRAPPED_SESSION_KEY)); String usageString = attributes.get(KEY_USAGE); if (! StringUtils.isBlank(usageString)) { @@ -96,7 +98,11 @@ public class SymKeyGenerationRequest extends ResourceMessage { * @return the keySize */ public Integer getKeySize() { - return new Integer(attributes.get(KEY_SIZE)); + try { + return new Integer(attributes.get(KEY_SIZE)); + } catch (NumberFormatException e) { + return null; + } } /** @@ -120,6 +126,20 @@ public class SymKeyGenerationRequest extends ResourceMessage { attributes.put(KEY_ALGORITHM, keyAlgorithm); } + /** + * @return the transWrappedSessionKey + */ + public String getTransWrappedSessionKey() { + return attributes.get(TRANS_WRAPPED_SESSION_KEY); + } + + /** + * @param transWrappedSessionKey the wrapped seesion key to set + */ + public void setTransWrappedSessionKey(String transWrappedSessionKey) { + attributes.put(TRANS_WRAPPED_SESSION_KEY, transWrappedSessionKey); + } + public String toString() { try { return ResourceMessage.marshal(this, SymKeyGenerationRequest.class); diff --git a/base/common/src/com/netscape/certsrv/request/IRequest.java b/base/common/src/com/netscape/certsrv/request/IRequest.java index e77a2e2b8..f9c442754 100644 --- a/base/common/src/com/netscape/certsrv/request/IRequest.java +++ b/base/common/src/com/netscape/certsrv/request/IRequest.java @@ -174,6 +174,7 @@ public interface IRequest extends Serializable { public static final String SYMKEY_GEN_ALGORITHM = "symkeyGenAlgorithm"; public static final String SYMKEY_GEN_SIZE = "symkeyGenSize"; public static final String SYMKEY_GEN_USAGES = "symkeyGenUsages"; + public static final String SYMKEY_TRANS_WRAPPED_SESSION_KEY = "transWrappedSessionKey"; // requestor type values. public static final String REQUESTOR_EE = "EE"; diff --git a/base/kra/functional/drmtest.py b/base/kra/functional/drmtest.py index a1db60c55..b53c5569a 100644 --- a/base/kra/functional/drmtest.py +++ b/base/kra/functional/drmtest.py @@ -106,18 +106,21 @@ def main(): # Test 4: generate symkey -- same as barbican_encode() print "Now generating symkey on KRA" #client_key_id = "Vek #1" + time.strftime('%X %x %Z') - client_key_id = "vek1234567" + client_key_id = "veka6" algorithm = "AES" key_size = 128 usages = [key.SymKeyGenerationRequest.DECRYPT_USAGE, key.SymKeyGenerationRequest.ENCRYPT_USAGE] - response = keyclient.generate_symmetric_key(client_key_id, algorithm, key_size, usages) + response = keyclient.generate_symmetric_key(client_key_id, + algorithm=algorithm, + size=key_size, + usages=usages) print_key_request(response.requestInfo) print "Request ID is " + response.requestInfo.get_request_id() key_id = response.get_key_id() # Test 5: Confirm the key_id matches print "Now getting key ID for clientKeyID=\"" + client_key_id + "\"" - key_infos = keyclient.list_keys(client_key_id=client_key_id, status="active") + key_infos = keyclient.list_keys(client_key_id=client_key_id, status=keyclient.KEY_STATUS_ACTIVE) for key_info in key_infos.key_infos: print_key_info(key_info) key_id2 = key_info.get_key_id() @@ -152,7 +155,10 @@ def main(): # Test 10 = test BadRequestException on create() print "Trying to generate a new symkey with the same client ID" try: - response = keyclient.generate_symmetric_key(client_key_id, algorithm, key_size, usages) + response = keyclient.generate_symmetric_key(client_key_id, + algorithm=algorithm, + size=key_size, + usages=usages) except pki.BadRequestException as exc: print "BadRequestException thrown - Code:" + exc.code + " Message: " + exc.message @@ -169,10 +175,6 @@ def main(): key_data, unwrapped_key = keyclient.retrieve_key('2000003434') except pki.KeyNotFoundException as exc: print "KeyNotFoundException thrown - Code:" + exc.code + " Message: " + exc.message - except pki.PKIException as exc: - # note: this is broken - we should be sending KeyNotFoundException here before the recovery - # request is created - to be fixed in next patch - print "PKIException thrown - Code:" + exc.code + " Message: " + exc.message #Test 13 = getKeyInfo print "Get key info for existing key" @@ -186,7 +188,7 @@ def main(): #Test 15: change the key status print "Change the key status" - keyclient.modify_key_status(key_id, "inactive") + keyclient.modify_key_status(key_id, keyclient.KEY_STATUS_INACTIVE) print_key_info(keyclient.get_key_info(key_id)) # Test 16: Get key info for non-existent key @@ -200,8 +202,14 @@ def main(): print "Get non-existent active key" try: key_info = keyclient.get_active_key_info(client_key_id) + print_key_info(key_info) except pki.ResourceNotFoundException as exc: print "ResourceNotFoundException thrown - Code: " + exc.code + "Message: " + exc.message + #Test 18: Generate a symmetric key with default parameters + client_key_id = "Vek #3" + time.strftime('%X %x %Z') + response = keyclient.generate_symmetric_key(client_key_id) + print_key_request(response.requestInfo) + if __name__ == "__main__": main() diff --git a/base/kra/src/CMakeLists.txt b/base/kra/src/CMakeLists.txt index 99088ce06..ccbc6a6c3 100644 --- a/base/kra/src/CMakeLists.txt +++ b/base/kra/src/CMakeLists.txt @@ -75,6 +75,13 @@ find_file(SYMKEY_JAR ${JAVA_LIB_INSTALL_DIR} ) +find_file(COMMONS_LANG_JAR + NAMES + commons-lang.jar + PATHS + /usr/share/java +) + # build pki-kra javac(pki-kra-classes @@ -84,7 +91,7 @@ javac(pki-kra-classes ${PKI_CERTSRV_JAR} ${PKI_CMS_JAR} ${PKI_CMSCORE_JAR} ${PKI_CMSUTIL_JAR} ${PKI_NSUTIL_JAR} ${LDAPJDK_JAR} ${JAXRS_API_JAR} - ${JSS_JAR} ${COMMONS_CODEC_JAR} ${SYMKEY_JAR} + ${JSS_JAR} ${COMMONS_CODEC_JAR} ${COMMONS_LANG_JAR} ${SYMKEY_JAR} OUTPUT_DIR ${CMAKE_BINARY_DIR}/classes DEPENDS diff --git a/base/kra/src/com/netscape/kra/SymKeyGenService.java b/base/kra/src/com/netscape/kra/SymKeyGenService.java index 99c57b8d9..d1e60fa70 100644 --- a/base/kra/src/com/netscape/kra/SymKeyGenService.java +++ b/base/kra/src/com/netscape/kra/SymKeyGenService.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.commons.lang.StringUtils; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.KeyGenAlgorithm; import org.mozilla.jss.crypto.KeyGenerator; @@ -92,7 +93,8 @@ public class SymKeyGenService implements IService { String algorithm = request.getExtDataInString(IRequest.SYMKEY_GEN_ALGORITHM); String usageStr = request.getExtDataInString(IRequest.SYMKEY_GEN_USAGES); - List<String> usages = new ArrayList<String>(Arrays.asList(usageStr.split(","))); + List<String> usages = new ArrayList<String>( + Arrays.asList(StringUtils.split(usageStr, ","))); String keySizeStr = request.getExtDataInString(IRequest.SYMKEY_GEN_SIZE); int keySize = Integer.parseInt(keySizeStr); diff --git a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java index 308d3daf8..d84bbd013 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java +++ b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java @@ -32,11 +32,13 @@ import org.mozilla.jss.crypto.KeyGenAlgorithm; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.BadRequestException; import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.dbs.EDBRecordNotFoundException; import com.netscape.certsrv.dbs.keydb.IKeyRecord; import com.netscape.certsrv.dbs.keydb.IKeyRepository; import com.netscape.certsrv.dbs.keydb.KeyId; import com.netscape.certsrv.key.KeyArchivalRequest; import com.netscape.certsrv.key.KeyData; +import com.netscape.certsrv.key.KeyNotFoundException; import com.netscape.certsrv.key.KeyRecoveryRequest; import com.netscape.certsrv.key.KeyRequestInfo; import com.netscape.certsrv.key.KeyRequestInfoCollection; @@ -142,7 +144,7 @@ public class KeyRequestDAO extends CMSRequestDAO { String keyAlgorithm = data.getKeyAlgorithm(); int keyStrength = data.getKeySize(); - boolean keyExists = doesKeyExist(clientKeyId, "active", uriInfo); + boolean keyExists = doesKeyExist(clientKeyId, "active"); if (keyExists == true) { throw new EBaseException("Can not archive already active existing key!"); @@ -184,6 +186,11 @@ public class KeyRequestDAO extends CMSRequestDAO { IRequest request = queue.newRequest(IRequest.SECURITY_DATA_RECOVERY_REQUEST); KeyId keyId = data.getKeyId(); + try { + repo.readKeyRecord(keyId.toBigInteger()); + } catch (EDBRecordNotFoundException e) { + throw new KeyNotFoundException(keyId); + } Hashtable<String, Object> requestParams; @@ -219,12 +226,13 @@ public class KeyRequestDAO extends CMSRequestDAO { String algName = data.getKeyAlgorithm(); Integer keySize = data.getKeySize(); List<String> usages = data.getUsages(); + String transWrappedSessionKey = data.getTransWrappedSessionKey(); if (StringUtils.isBlank(clientKeyId)) { throw new BadRequestException("Invalid key generation request. Missing client ID"); } - boolean keyExists = doesKeyExist(clientKeyId, "active", uriInfo); + boolean keyExists = doesKeyExist(clientKeyId, "active"); if (keyExists == true) { throw new BadRequestException("Can not archive already active existing key!"); } @@ -261,6 +269,11 @@ public class KeyRequestDAO extends CMSRequestDAO { request.setExtData(IRequest.SYMKEY_GEN_USAGES, StringUtils.join(usages, ",")); request.setExtData(IRequest.SECURITY_DATA_CLIENT_KEY_ID, clientKeyId); + if (transWrappedSessionKey != null) { + request.setExtData(IRequest.SYMKEY_TRANS_WRAPPED_SESSION_KEY, + transWrappedSessionKey); + } + queue.processRequest(request); queue.markAsServiced(request); @@ -331,10 +344,14 @@ public class KeyRequestDAO extends CMSRequestDAO { } //We only care if the key exists or not - private boolean doesKeyExist(String clientKeyId, String keyStatus, UriInfo uriInfo) { - String state = "active"; - String filter = "(&(" + IRequest.SECURITY_DATA_CLIENT_KEY_ID + "=" + clientKeyId + ")" - + "(" + IRequest.SECURITY_DATA_STATUS + "=" + state + "))"; + private boolean doesKeyExist(String clientKeyId, String keyStatus) { + String filter = null; + if (keyStatus == null) { + filter = "(" + IKeyRecord.ATTR_CLIENT_ID + "=" + clientKeyId + ")"; + } else { + filter = "(&(" + IKeyRecord.ATTR_CLIENT_ID + "=" + clientKeyId + ")" + + "(" + IKeyRecord.ATTR_STATUS + "=" + keyStatus + "))"; + } try { Enumeration<IKeyRecord> existingKeys = null; |