diff options
25 files changed, 1081 insertions, 174 deletions
diff --git a/base/common/python/pki/key.py b/base/common/python/pki/key.py index af34a7ff4..0be438a28 100644 --- a/base/common/python/pki/key.py +++ b/base/common/python/pki/key.py @@ -105,7 +105,7 @@ class KeyInfo(object): json_attribute_names = { 'clientKeyID': 'client_key_id', 'keyURL': 'key_url', - 'ownerName': 'owner_name' + 'ownerName': 'owner_name', 'publicKey': 'public_key' } # pylint: disable-msg=C0103 @@ -117,6 +117,7 @@ class KeyInfo(object): self.status = None self.owner_name = None self.size = None + self.public_key = None @classmethod def from_json(cls, attr_list): @@ -127,6 +128,8 @@ class KeyInfo(object): setattr(key_info, KeyInfo.json_attribute_names[k], v) else: setattr(key_info, k, v) + if key_info.public_key is not None: + key_info.public_key = base64.decodestring(key_info.public_key) return key_info def get_key_id(self): @@ -339,7 +342,7 @@ class KeyRecoveryRequest(pki.ResourceMessage): class SymKeyGenerationRequest(pki.ResourceMessage): """ Class representing the data sent to the DRM when generating and archiving - a symmetric key on the DRM. + a symmetric key in the DRM. """ UNWRAP_USAGE = "unwrap" @@ -363,6 +366,36 @@ class SymKeyGenerationRequest(pki.ResourceMessage): self.add_attribute("transWrappedSessionKey", trans_wrapped_session_key) +class AsymKeyGenerationRequest(pki.ResourceMessage): + + """ + Class representing the data sent to the DRM when generating and archiving + asymmetric keys in the DRM. + """ + UNWRAP_USAGE = "unwrap" + WRAP_USAGE = "wrap" + VERIFY_USAGE = "verify" + VERIFY_RECOVER_USAGE = "verify_recover" + SIGN_USAGE = "sign" + SIGN_RECOVER_USAGE = "sign_recover" + DECRYPT_USAGE = "decrypt" + ENCRYPT_USAGE = "encrypt" + DERIVE_USAGE = "derive" + + def __init__(self, client_key_id=None, key_size=None, key_algorithm=None, + key_usages=None, trans_wrapped_session_key=None): + """ Constructor """ + pki.ResourceMessage.__init__( + self, + "com.netscape.certsrv.key.AsymKeyGenerationRequest") + key_usages = key_usages or [] + self.add_attribute("clientKeyID", client_key_id) + 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): """ Class that encapsulates and mirrors the functions in the KeyResource @@ -383,6 +416,10 @@ class KeyClient(object): RC4_ALGORITHM = "RC4" AES_ALGORITHM = "AES" + # Asymmetric Key Algorithms + RSA_ALGORITHM = "RSA" + DSA_ALGORITHM = "DSA" + #default session key wrapping algorithm DES_EDE3_CBC_OID = "{1 2 840 113549 3 7}" @@ -509,12 +546,13 @@ class KeyClient(object): self.connection.post(url, None, self.headers) @pki.handle_exceptions() - def create_request(self, request): + def submit_request(self, request): """ Submit an archival, recovery or key generation request to the DRM. @param request - is either a KeyArchivalRequest, - KeyRecoverRequest or SymKeyGenerationRequest. + KeyRecoverRequest, SymKeyGenerationRequest or + AsymKeyGenerationRequest. returns a KeyRequestResponse object. """ @@ -558,7 +596,57 @@ class KeyClient(object): key_size=size, key_algorithm=algorithm, key_usages=usages) - return self.create_request(request) + return self.submit_request(request) + + @pki.handle_exceptions() + def generate_asymmetric_key(self, client_key_id, algorithm=None, + key_size=None, usages=None, + trans_wrapped_session_key=None): + """ Generate and archive asymmetric keys in the DRM. + Supports algorithms RSA and DSA. + Valid key size for RSA = 256 + (16 * n), where n: 0-496 + Valid key size for DSA = 512, 768, 1024. p,q,g params are not + supported. + + Return a KeyRequestResponse which contains a KeyRequestInfo + object that describes the URL for the request and generated keys. + + """ + if client_key_id is None: + raise TypeError("Must specify Client Key ID") + + if str(algorithm).upper() not in \ + [self.RSA_ALGORITHM, self.DSA_ALGORITHM]: + raise TypeError("Only RSA and DSA algorithms are supported.") + + # For generating keys using the RSA algorithm, the valid range of key + # sizes is: + # 256 + 16 * n, where 0 <= n <= 1008 + # When using DSA, the current supported values are 512, 768, 1024 + + if algorithm == self.RSA_ALGORITHM: + if key_size < 256: + raise ValueError("Invalid key size specified.") + if ((key_size-256) % 16) != 0: + raise ValueError("Invalid key size specified.") + if algorithm == self.DSA_ALGORITHM: + if key_size not in [512, 768, 1024]: + raise ValueError("Invalid key size specified.") + + if trans_wrapped_session_key is not None: + raise NotImplementedError( + "Returning the asymmetric keys in the same call is not yet " + "implemented.") + + request = AsymKeyGenerationRequest( + client_key_id=client_key_id, + key_size=key_size, + key_algorithm=algorithm, + key_usages=usages, + trans_wrapped_session_key=trans_wrapped_session_key + ) + + return self.submit_request(request) @pki.handle_exceptions() def archive_key(self, client_key_id, data_type, private_data, @@ -666,7 +754,7 @@ class KeyClient(object): key_algorithm=key_algorithm, key_size=key_size) - return self.create_request(request) + return self.submit_request(request) @pki.handle_exceptions() def archive_pki_options(self, client_key_id, data_type, pki_archive_options, @@ -701,7 +789,7 @@ class KeyClient(object): pki_archive_options=data, key_algorithm=key_algorithm, key_size=key_size) - return self.create_request(request) + return self.submit_request(request) @pki.handle_exceptions() def recover_key(self, key_id, request_id=None, @@ -729,7 +817,7 @@ class KeyClient(object): session_wrapped_passphrase=session_wrapped_passphrase, certificate=b64certificate, nonce_data=nonce_data) - return self.create_request(request) + return self.submit_request(request) @pki.handle_exceptions() def retrieve_key_data(self, data): @@ -770,9 +858,10 @@ class KeyClient(object): 1) trans_wrapped_session_key is not provided by caller. - In this case, the function will call CryptoProvider methods to generate and - wrap the session key. The function will return the KeyData object with - a private_data attribute which stores the unwrapped key information. + In this case, the function will call CryptoProvider methods to generate + and wrap the session key. The function will return the KeyData object + with a private_data attribute which stores the unwrapped key + information. 2) The trans_wrapped_session_key is provided by the caller. @@ -833,8 +922,8 @@ class KeyClient(object): 1) A passphrase is provided by the caller. - In this case, CryptoProvider methods will be called to create the data to - securely send the passphrase to the DRM. Basically, three pieces of + In this case, CryptoProvider 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 168 bit 3DES symmetric key (the session @@ -894,6 +983,7 @@ encoder.NOTYPES['KeyArchivalRequest'] = KeyArchivalRequest encoder.NOTYPES['KeyRecoveryRequest'] = KeyRecoveryRequest encoder.NOTYPES['ResourceMessage'] = pki.ResourceMessage encoder.NOTYPES['SymKeyGenerationRequest'] = SymKeyGenerationRequest +encoder.NOTYPES['AsymKeyGenerationRequest'] = AsymKeyGenerationRequest def main(): diff --git a/base/common/src/com/netscape/certsrv/base/ResourceMessage.java b/base/common/src/com/netscape/certsrv/base/ResourceMessage.java index 34d7c2b11..1214b45fb 100644 --- a/base/common/src/com/netscape/certsrv/base/ResourceMessage.java +++ b/base/common/src/com/netscape/certsrv/base/ResourceMessage.java @@ -26,6 +26,7 @@ import javax.xml.bind.annotation.XmlValue; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import com.netscape.certsrv.key.AsymKeyGenerationRequest; import com.netscape.certsrv.key.KeyArchivalRequest; import com.netscape.certsrv.key.KeyRecoveryRequest; import com.netscape.certsrv.key.SymKeyGenerationRequest; @@ -33,8 +34,9 @@ import com.netscape.certsrv.key.SymKeyGenerationRequest; /** * @author Ade Lee */ -@XmlRootElement(name="ResourceMessage") -@XmlSeeAlso({KeyArchivalRequest.class, KeyRecoveryRequest.class, SymKeyGenerationRequest.class, PKIException.Data.class}) +@XmlRootElement(name = "ResourceMessage") +@XmlSeeAlso({ KeyArchivalRequest.class, KeyRecoveryRequest.class, SymKeyGenerationRequest.class, + PKIException.Data.class, AsymKeyGenerationRequest.class }) @XmlAccessorType(XmlAccessType.NONE) public class ResourceMessage { @@ -46,7 +48,7 @@ public class ResourceMessage { } public ResourceMessage(MultivaluedMap<String, String> form) { - for (Map.Entry<String, List<String>> entry: form.entrySet()) { + for (Map.Entry<String, List<String>> entry : form.entrySet()) { attributes.put(entry.getKey(), entry.getValue().get(0)); } } diff --git a/base/common/src/com/netscape/certsrv/key/AsymKeyGenerationRequest.java b/base/common/src/com/netscape/certsrv/key/AsymKeyGenerationRequest.java new file mode 100644 index 000000000..867c06acf --- /dev/null +++ b/base/common/src/com/netscape/certsrv/key/AsymKeyGenerationRequest.java @@ -0,0 +1,115 @@ +//--- BEGIN COPYRIGHT BLOCK --- +//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. +// +//(C) 2014 Red Hat, Inc. +//All rights reserved. +//--- END COPYRIGHT BLOCK --- +package com.netscape.certsrv.key; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.ws.rs.core.MultivaluedMap; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.commons.lang.StringUtils; + +import com.netscape.certsrv.base.ResourceMessage; + +@XmlRootElement(name = "AsymKeyGenerationRequest") +@XmlAccessorType(XmlAccessType.FIELD) +public class AsymKeyGenerationRequest extends KeyGenerationRequest { + + // Asymmetric Key Usages + public static final String ENCRYPT = "encrypt"; + public static final String DECRYPT = "decrypt"; + public static final String SIGN = "sign"; + public static final String SIGN_RECOVER = "sign_recover"; + public static final String VERIFY = "verify"; + public static final String VERIFY_RECOVER = "verify_recover"; + public static final String WRAP = "wrap"; + public static final String UNWRAP = "unwrap"; + public static final String DERIVE = "derive"; + + public AsymKeyGenerationRequest() { + // required for JAXB (defaults) + setClassName(getClass().getName()); + } + + public AsymKeyGenerationRequest(MultivaluedMap<String, String> form) { + attributes.put(CLIENT_KEY_ID, form.getFirst(CLIENT_KEY_ID)); + 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)) { + setUsages(new ArrayList<String>(Arrays.asList(usageString.split(",")))); + } + setClassName(getClass().getName()); + } + + public AsymKeyGenerationRequest(ResourceMessage data) { + attributes.putAll(data.getAttributes()); + setClassName(getClass().getName()); + } + + public String toString() { + try { + return ResourceMessage.marshal(this, AsymKeyGenerationRequest.class); + } catch (Exception e) { + return super.toString(); + } + } + + public static AsymKeyGenerationRequest valueOf(String string) throws Exception { + try { + return ResourceMessage.unmarshal(string, AsymKeyGenerationRequest.class); + } catch (Exception e) { + return null; + } + } + + public static List<String> getValidUsagesList() { + List<String> list = new ArrayList<String>(); + list.add(DERIVE); + list.add(SIGN); + list.add(DECRYPT); + list.add(ENCRYPT); + list.add(WRAP); + list.add(UNWRAP); + list.add(SIGN_RECOVER); + list.add(VERIFY); + list.add(VERIFY_RECOVER); + + return list; + } + + public static void main(String[] args) { + AsymKeyGenerationRequest request = new AsymKeyGenerationRequest(); + request.setKeyAlgorithm(KeyRequestResource.RSA_ALGORITHM); + request.setKeySize(1024); + request.setClientKeyId("vek12345"); + List<String> usages = new ArrayList<String>(); + usages.add(AsymKeyGenerationRequest.ENCRYPT); + usages.add(AsymKeyGenerationRequest.DECRYPT); + request.setUsages(usages); + + System.out.println(request.toString()); + } +} diff --git a/base/common/src/com/netscape/certsrv/key/KeyClient.java b/base/common/src/com/netscape/certsrv/key/KeyClient.java index 9363a6a8c..262a33d8f 100644 --- a/base/common/src/com/netscape/certsrv/key/KeyClient.java +++ b/base/common/src/com/netscape/certsrv/key/KeyClient.java @@ -249,11 +249,11 @@ public class KeyClient extends Client { * @param data -- A KeyArchivalRequest/KeyRecoveryRequest/SymKeyGenerationRequest object * @return A KeyRequestResponse object */ - private KeyRequestResponse createRequest(ResourceMessage request) { + private KeyRequestResponse submitRequest(ResourceMessage request) { if (request == null) { throw new IllegalArgumentException("A Request object must be specified."); } - Response response = keyRequestClient.createRequest(request); + Response response = keyRequestClient.submitRequest(request); return client.getEntity(response, KeyRequestResponse.class); } @@ -296,7 +296,7 @@ public class KeyClient extends Client { data.setCertificate(b64Certificate); } - return createRequest(data); + return submitRequest(data); } /** @@ -612,7 +612,7 @@ public class KeyClient extends Client { data.setWrappedPrivateData(req1); data.setTransWrappedSessionKey(Utils.base64encode(transWrappedSessionKey)); - return createRequest(data); + return submitRequest(data); } /** @@ -653,15 +653,15 @@ public class KeyClient extends Client { String options = Utils.base64encode(pkiArchiveOptions); data.setPKIArchiveOptions(options); - return createRequest(data); + return submitRequest(data); } /** - * Generate and archive a symmetric key on the DRM. + * Generate and archive a symmetric key in the DRM. * * @param clientKeyId -- Client Key Identifier * @param keyAlgorithm -- Algorithm to be used to generate the key - * @param keySize -- Strength of the algorithm + * @param keySize -- Strength of the keys * @param usages -- Usages of the generated key. * @return a KeyRequestResponse which contains a KeyRequestInfo * object that describes the URL for the request and generated key. @@ -687,6 +687,66 @@ public class KeyClient extends Client { data.setUsages(usages); data.setTransWrappedSessionKey(transWrappedSessionKey); - return createRequest(data); + return submitRequest(data); + } + + /** + * Generate and archive an asymmetric keys in the DRM + * + * @param clientKeyId -- Client Key Identifier + * @param keyAlgorithm -- Algorithm to be used to generate the asymmetric keys + * @param keySize -- Strength of the keys + * @param usages + * @param transWrappedSessionKey + * @return + */ + public KeyRequestResponse generateAsymmetricKey(String clientKeyId, String keyAlgorithm, int keySize, + List<String> usages, byte[] transWrappedSessionKey) { + + if (clientKeyId == null) { + throw new IllegalArgumentException("Client Key Identifier must be specified."); + } + + //Validate the usages list + List<String> validUsages = AsymKeyGenerationRequest.getValidUsagesList(); + if (usages != null) { + for (String usage : usages) { + if (!validUsages.contains(usage)) { + throw new IllegalArgumentException("Invalid usage \"" + usage + "\" specified."); + } + } + } + if (!(keyAlgorithm.equals(KeyRequestResource.RSA_ALGORITHM) || keyAlgorithm + .equals(KeyRequestResource.DSA_ALGORITHM))) { + throw new IllegalArgumentException("Unsupported algorithm specified."); + } + + /* + * For RSA, JSS accepts key sizes that fall in this set of values: + * {256 + (16 * n), where 0 <= n <= 1008 + * + * For DSA, JSS accepts key sizes 512, 768, 1024 only when there are no p,q,g params specified. + */ + if (keyAlgorithm.equals(KeyRequestResource.RSA_ALGORITHM)) { + if (keySize >= 256) { + if ((keySize - 256) % 16 != 0) { + throw new IllegalArgumentException("Invalid key size specified."); + } + } else { + throw new IllegalArgumentException("Invalid key size specified."); + } + } else if (keyAlgorithm.equals(KeyRequestResource.DSA_ALGORITHM)) { + if (keySize != 512 && keySize != 768 && keySize != 1024) { + throw new IllegalArgumentException("Invalid key size specified."); + } + } + AsymKeyGenerationRequest data = new AsymKeyGenerationRequest(); + data.setClientKeyId(clientKeyId); + data.setKeyAlgorithm(keyAlgorithm); + data.setKeySize(keySize); + data.setUsages(usages); + data.setTransWrappedSessionKey(Utils.base64encode(transWrappedSessionKey)); + + return submitRequest(data); } } diff --git a/base/common/src/com/netscape/certsrv/key/KeyGenerationRequest.java b/base/common/src/com/netscape/certsrv/key/KeyGenerationRequest.java new file mode 100644 index 000000000..ed36b6d9d --- /dev/null +++ b/base/common/src/com/netscape/certsrv/key/KeyGenerationRequest.java @@ -0,0 +1,125 @@ +//--- BEGIN COPYRIGHT BLOCK --- +//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. +// +//(C) 2014 Red Hat, Inc. +//All rights reserved. +//--- END COPYRIGHT BLOCK --- +package com.netscape.certsrv.key; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang.StringUtils; + +import com.netscape.certsrv.base.ResourceMessage; + +/** + * Class to define the common attributes and methods used by + * SymKeyGenerationRequest and AsymKeyGenerationRequest + * @author akoneru + * + */ +public class KeyGenerationRequest extends ResourceMessage{ + + protected static final String CLIENT_KEY_ID = "clientKeyID"; + protected static final String KEY_SIZE = "keySize"; + protected static final String KEY_ALGORITHM = "keyAlgorithm"; + protected static final String KEY_USAGE = "keyUsage"; + protected static final String TRANS_WRAPPED_SESSION_KEY = "transWrappedSessionKey"; + + + public List<String> getUsages() { + String usageString = attributes.get(KEY_USAGE); + if (!StringUtils.isBlank(usageString)) { + return new ArrayList<String>(Arrays.asList(usageString.split(","))); + } + return new ArrayList<String>(); + } + + public void setUsages(List<String> usages) { + attributes.put(KEY_USAGE, StringUtils.join(usages, ",")); + } + + public void addUsage(String usage) { + List<String> usages = getUsages(); + for (String u : usages) { + if (u.equals(usage)) + return; + } + usages.add(usage); + setUsages(usages); + } + + /** + * @return the clientKeyId + */ + public String getClientKeyId() { + return attributes.get(CLIENT_KEY_ID); + } + + /** + * @param clientKeyId the clientKeyId to set + */ + public void setClientKeyId(String clientKeyId) { + attributes.put(CLIENT_KEY_ID, clientKeyId); + } + + /** + * @return the keySize + */ + public Integer getKeySize() { + try { + return new Integer(attributes.get(KEY_SIZE)); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * @param keySize the key size to set + */ + public void setKeySize(Integer keySize) { + attributes.put(KEY_SIZE, keySize.toString()); + } + + /** + * @return the keyAlgorithm + */ + public String getKeyAlgorithm() { + return attributes.get(KEY_ALGORITHM); + } + + /** + * @param keyAlgorithm the key algorithm to set + */ + public void setKeyAlgorithm(String keyAlgorithm) { + 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); + } + +} diff --git a/base/common/src/com/netscape/certsrv/key/KeyInfo.java b/base/common/src/com/netscape/certsrv/key/KeyInfo.java index 10da545d8..71a858e6b 100644 --- a/base/common/src/com/netscape/certsrv/key/KeyInfo.java +++ b/base/common/src/com/netscape/certsrv/key/KeyInfo.java @@ -54,6 +54,9 @@ public class KeyInfo { @XmlElement protected String ownerName; + @XmlElement + protected String publicKey; + public KeyInfo() { // required for JAXB (defaults) } @@ -125,4 +128,12 @@ public class KeyInfo { public void setOwnerName(String ownerName) { this.ownerName = ownerName; } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } } diff --git a/base/common/src/com/netscape/certsrv/key/KeyRequestResource.java b/base/common/src/com/netscape/certsrv/key/KeyRequestResource.java index fb82afe19..768127e42 100644 --- a/base/common/src/com/netscape/certsrv/key/KeyRequestResource.java +++ b/base/common/src/com/netscape/certsrv/key/KeyRequestResource.java @@ -35,6 +35,11 @@ public interface KeyRequestResource { public static final String RC4_ALGORITHM = "RC4"; public static final String AES_ALGORITHM = "AES"; + // Asymmetric Key algorithms + public final static String RSA_ALGORITHM = "RSA"; + public final static String DSA_ALGORITHM = "DSA"; + public final static String EC_ALGORITHM = "EC"; // Not supported yet. + /** * Used to generate list of key requests based on the search parameters */ @@ -51,11 +56,11 @@ public interface KeyRequestResource { @POST @ClientResponseType(entityType=KeyRequestResponse.class) @Consumes({ MediaType.APPLICATION_FORM_URLENCODED}) - public Response createRequest(MultivaluedMap<String, String> form); + public Response submitRequest(MultivaluedMap<String, String> form); @POST @ClientResponseType(entityType=KeyRequestResponse.class) - public Response createRequest(ResourceMessage data); + public Response submitRequest(ResourceMessage data); /** * Used to retrieve key request info for a specific request diff --git a/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java b/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java index a2440d7cb..7f65d0e59 100644 --- a/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java +++ b/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java @@ -19,13 +19,7 @@ import com.netscape.certsrv.base.ResourceMessage; */ @XmlRootElement(name = "SymKeyGenerationRequest") @XmlAccessorType(XmlAccessType.FIELD) -public class SymKeyGenerationRequest extends ResourceMessage { - - private static final String CLIENT_KEY_ID = "clientKeyID"; - 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"; +public class SymKeyGenerationRequest extends KeyGenerationRequest { /* Symmetric Key usages */ public static final String UWRAP_USAGE = "unwrap"; @@ -35,28 +29,6 @@ public class SymKeyGenerationRequest extends ResourceMessage { public static final String DECRYPT_USAGE = "decrypt"; public static final String ENCRYPT_USAGE = "encrypt"; - public List<String> getUsages() { - String usageString = attributes.get(KEY_USAGE); - if (!StringUtils.isBlank(usageString)) { - return new ArrayList<String>(Arrays.asList(usageString.split(","))); - } - return new ArrayList<String>(); - } - - public void setUsages(List<String> usages) { - attributes.put(KEY_USAGE, StringUtils.join(usages, ",")); - } - - public void addUsage(String usage) { - List<String> usages = getUsages(); - for (String u : usages) { - if (u.equals(usage)) - return; - } - usages.add(usage); - setUsages(usages); - } - public SymKeyGenerationRequest() { // required for JAXB (defaults) setClassName(getClass().getName()); @@ -82,59 +54,6 @@ public class SymKeyGenerationRequest extends ResourceMessage { } /** - * @return the clientKeyId - */ - public String getClientKeyId() { - return attributes.get(CLIENT_KEY_ID); - } - - /** - * @param clientKeyId the clientKeyId to set - */ - public void setClientKeyId(String clientKeyId) { - attributes.put(CLIENT_KEY_ID, clientKeyId); - } - - /** - * @return the keySize - */ - public Integer getKeySize() { - try { - return new Integer(attributes.get(KEY_SIZE)); - } catch (NumberFormatException e) { - return null; - } - } - - /** - * @param keySize the key size to set - */ - public void setKeySize(Integer keySize) { - attributes.put(KEY_SIZE, keySize.toString()); - } - - /** - * @return the keyAlgorithm - */ - public String getKeyAlgorithm() { - return attributes.get(KEY_ALGORITHM); - } - - /** - * @param keyAlgorithm the key algorithm to set - */ - public void setKeyAlgorithm(String keyAlgorithm) { - 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) { diff --git a/base/common/src/com/netscape/certsrv/request/IRequest.java b/base/common/src/com/netscape/certsrv/request/IRequest.java index 885cb72a6..8d4ec98fb 100644 --- a/base/common/src/com/netscape/certsrv/request/IRequest.java +++ b/base/common/src/com/netscape/certsrv/request/IRequest.java @@ -169,12 +169,14 @@ public interface IRequest extends Serializable { public static final String SECURITY_DATA_SESS_WRAPPED_DATA = "sessWrappedSecData"; public static final String SECURITY_DATA_PASS_WRAPPED_DATA = "passPhraseWrappedData"; - // symkey generation request attributes + // key generation request attributes + public static final String ASYMKEY_GENERATION_REQUEST = "asymkeyGenRequest"; public static final String SYMKEY_GENERATION_REQUEST = "symkeyGenRequest"; - 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"; + + public static final String KEY_GEN_ALGORITHM = "keyGenAlgorithm"; + public static final String KEY_GEN_SIZE = "keyGenSize"; + public static final String KEY_GEN_USAGES = "keyGenUsages"; + public static final String KEY_GEN_TRANS_WRAPPED_SESSION_KEY = "transWrappedSessionKey"; // requestor type values. public static final String REQUESTOR_EE = "EE"; diff --git a/base/java-tools/src/com/netscape/cmstools/key/KeyCLI.java b/base/java-tools/src/com/netscape/cmstools/key/KeyCLI.java index 1b8ae64b5..82235d278 100644 --- a/base/java-tools/src/com/netscape/cmstools/key/KeyCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/key/KeyCLI.java @@ -93,6 +93,18 @@ public class KeyCLI extends CLI { if (info.getAlgorithm() != null) System.out.println(" Algorithm: "+info.getAlgorithm()); if (info.getSize() != null) System.out.println(" Size: "+info.getSize()); if (info.getOwnerName() != null) System.out.println(" Owner: "+info.getOwnerName()); + if (info.getPublicKey() != null) { + // Print out the Base64 encoded public key in the form of a blob, + // where the max line length is 64. + System.out.println(" Public Key: \n"); + String publicKey = info.getPublicKey(); + int i = 0; + for(i=0;i<publicKey.length()/64;i++){ + System.out.println(publicKey.substring(i*64, i*64 + 64)); + } + System.out.println(publicKey.substring(i*64)); + System.out.println(); + } } public static void printKeyRequestInfo(KeyRequestInfo info) { diff --git a/base/java-tools/src/com/netscape/cmstools/key/KeyGenerateCLI.java b/base/java-tools/src/com/netscape/cmstools/key/KeyGenerateCLI.java index a551d61f3..c8608731e 100644 --- a/base/java-tools/src/com/netscape/cmstools/key/KeyGenerateCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/key/KeyGenerateCLI.java @@ -28,7 +28,7 @@ public class KeyGenerateCLI extends CLI { public void createOptions() { Option option = new Option(null, "key-algorithm", true, - "Algorithm to be used to create a key.\nValid values: AES, DES, DES3, RC2, RC4, DESede."); + "Algorithm to be used to create a key.\nValid values: AES, DES, DES3, RC2, RC4, DESede, RSA, DSA"); option.setArgName("algorithm"); option.setRequired(true); options.addOption(option); @@ -38,12 +38,14 @@ public class KeyGenerateCLI extends CLI { "key-size", true, "Size of the key to be generated.\nThis is required for AES, RC2 and RC4.\n" - + "Valid values for AES: 128, 192. 256.\nValid values for RC2: 8-128.\n Valid values for RC4: Any positive integer."); + + "Valid values for AES: 128, 192. 256.\nValid values for RC2: 8-128.\n Valid values for RC4: Any positive integer." + + "\n Valid values for DSA: 512, 768, 1024.\nValid values for RSA: 256 + (16*n), n= [0-496]"); option.setArgName("size"); options.addOption(option); option = new Option(null, "usages", true, "Comma separated list of usages." - + "\nValid values: wrap, unwrap, sign, verify, encrypt, decrypt."); + + "\nValid values: wrap, unwrap, sign, verify, encrypt, decrypt." + + "\nAdditional usages for RSA and DSA type keys: derive, sign_recover, verify_recover."); option.setArgName("list of usages"); options.addOption(option); } @@ -91,6 +93,8 @@ public class KeyGenerateCLI extends CLI { case KeyRequestResource.RC4_ALGORITHM: case KeyRequestResource.AES_ALGORITHM: case KeyRequestResource.RC2_ALGORITHM: + case KeyRequestResource.RSA_ALGORITHM: + case KeyRequestResource.DSA_ALGORITHM: System.err.println("Error: Key size must be specified for the algorithm used."); printHelp(); System.exit(-1); @@ -100,16 +104,45 @@ public class KeyGenerateCLI extends CLI { System.exit(-1); } } + + int size = 0; + try { + size = Integer.parseInt(keySize); + } catch (NumberFormatException e) { + System.err.println("Error: Key size must be an integer."); + printHelp(); + System.exit(-1); + } List<String> usages = null; String givenUsages = cmd.getOptionValue("usages"); if (givenUsages != null) { usages = Arrays.asList(givenUsages.split(",")); } - KeyRequestResponse response = keyCLI.keyClient.generateSymmetricKey(clientKeyId, keyAlgorithm, - Integer.parseInt(keySize), - usages, null); - + KeyRequestResponse response = null; + switch (keyAlgorithm) { + case KeyRequestResource.DES3_ALGORITHM: + case KeyRequestResource.DESEDE_ALGORITHM: + case KeyRequestResource.DES_ALGORITHM: + case KeyRequestResource.RC4_ALGORITHM: + case KeyRequestResource.AES_ALGORITHM: + case KeyRequestResource.RC2_ALGORITHM: + response = keyCLI.keyClient.generateSymmetricKey(clientKeyId, keyAlgorithm, + size, + usages, null); + break; + case KeyRequestResource.RSA_ALGORITHM: + case KeyRequestResource.DSA_ALGORITHM: + response = keyCLI.keyClient.generateAsymmetricKey(clientKeyId, keyAlgorithm, + size, + usages, null); + break; + default: + System.err.println("Error: Algorithm not supported."); + printHelp(); + System.exit(-1); + } MainCLI.printMessage("Key generation request info"); KeyCLI.printKeyRequestInfo(response.getRequestInfo()); } + } diff --git a/base/kra/functional/drmtest.py b/base/kra/functional/drmtest.py index b309ce09f..4d65955f9 100644 --- a/base/kra/functional/drmtest.py +++ b/base/kra/functional/drmtest.py @@ -57,6 +57,12 @@ def print_key_info(key_info): print "Status: " + str(key_info.status) print "Owner Name: " + str(key_info.owner_name) print "Size: " + str(key_info.size) + if key_info.public_key is not None: + print "Public key: " + print + pub_key = str(key_info.public_key) + for i in range(0, len(pub_key), 64): + print pub_key[i:i+64] def print_key_data(key_data): @@ -81,7 +87,7 @@ def main(): certdb_dir = "/tmp/drmtest-certdb" certdb_password = "redhat123" pki.crypto.NSSCryptoProvider.setup_database(certdb_dir, certdb_password, - over_write=True) + over_write=True) #create kraclient crypto = pki.crypto.NSSCryptoProvider(certdb_dir, certdb_password) @@ -260,6 +266,26 @@ def main(): print "Success: archived and recovered keys match" else: print "Error: archived and recovered keys do not match" + print + + #Test 20: Generating asymmetric keys + print "Generating asymmetric keys" + try: + response = keyclient.generate_asymmetric_key( + "Vek #5" + time.strftime('%c'), + algorithm="RSA", + key_size=1024, + usages=None + ) + print_key_request(response.request_info) + except pki.BadRequestException as exc: + print "BadRequestException thrown - Code:" + exc.code +\ + " Message: " + exc.message + + #Test 21: Get key information of the newly generated asymmetric keys + print "Retrieving key information" + key_info = keyclient.get_key_info(response.request_info.get_key_id()) + print_key_info(key_info) if __name__ == "__main__": main() diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java index cb80039b7..1b96c1809 100644 --- a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java +++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java @@ -17,6 +17,17 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cms.servlet.test; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Calendar; import java.util.Iterator; @@ -40,6 +51,7 @@ import com.netscape.certsrv.cert.CertData; import com.netscape.certsrv.client.ClientConfig; import com.netscape.certsrv.client.PKIClient; import com.netscape.certsrv.dbs.keydb.KeyId; +import com.netscape.certsrv.key.AsymKeyGenerationRequest; import com.netscape.certsrv.key.Key; import com.netscape.certsrv.key.KeyClient; import com.netscape.certsrv.key.KeyInfo; @@ -66,7 +78,8 @@ public class DRMTest { System.exit(1); } - public static void main(String args[]) { + public static void main(String args[]) throws InvalidKeyException, NoSuchAlgorithmException, + InvalidKeySpecException, SignatureException, IOException { String host = null; String port = null; String token_pwd = null; @@ -648,6 +661,84 @@ public class DRMTest { } catch (ResourceNotFoundException e) { log("Success: ResourceNotFound exception thrown: " + e); } + + // Test asymmetric key generation. + + String[] algs = { "RSA", "DSA" }; + for (int i = 0; i < algs.length; i++) { + // Test 30: Generate Asymmetric keys - RSA key + System.out.println("\nTesting asymmetric key generation for algorithm " + algs[i]); + clientKeyId = "AsymKey #" + Calendar.getInstance().getTimeInMillis(); + usages.clear(); + usages.add(AsymKeyGenerationRequest.SIGN); + usages.add(AsymKeyGenerationRequest.VERIFY); + KeyRequestResponse response = keyClient.generateAsymmetricKey(clientKeyId, algs[i], 1024, usages, null); + printRequestInfo(response.getRequestInfo()); + System.out.println(); + + // Test 31: Get information of the newly generated asymmetric keys + System.out.println("Fetch information of the newly generated asymmetric keys."); + System.out.println(); + KeyInfo info = keyClient.getKeyInfo(response.getKeyId()); + printKeyInfo(info); + System.out.println(); + + // Test 32: Retrieve private key data + System.out.println("Retrieving and verifying the generated private key."); + try { + keyData = keyClient.retrieveKey(response.getKeyId()); + } catch (Exception e) { + log("Exception retrieving the private key data."); + e.printStackTrace(); + } + + // Test 33: Verify the generated key pair. + if (isKeyPairValid(algs[i], keyData.getData(), Utils.base64decode(info.getPublicKey()))) { + log("The key pair generated using " + algs[i] + " algorithm is valid."); + } else { + log("The key pair generated using " + algs[i] + " algorithm is invalid."); + } + System.out.println(); + } + + // Test 34: + } + + /** + * Verify the generated asymmetric key pair. + * + * @param keyAlgorithm - Algorithm used to generate keys. + * @param privateKey - binary data of the private key. + * @param publicKey - binary data of he public key. + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws InvalidKeyException + * @throws SignatureException + * @throws IOException + */ + public static boolean isKeyPairValid(String keyAlgorithm, byte[] privateKey, byte[] publicKey) + throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException, + IOException { + String algorithm = keyAlgorithm.toUpperCase(); + String signingAlgorithm = "SHA1with" + algorithm; + KeyFactory factory = KeyFactory.getInstance(algorithm); + PrivateKey priKey = factory.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); + PublicKey pubKey = factory.generatePublic(new X509EncodedKeySpec(publicKey)); + Signature sig = Signature.getInstance(signingAlgorithm); + sig.initSign(priKey); + String s = "Data to test asymmetric keys."; + sig.update(s.getBytes()); + + // Sign the data with the private key. + byte[] realSig = sig.sign(); + + Signature sig2 = Signature.getInstance(signingAlgorithm); + sig2.initVerify(pubKey); + + sig2.update(s.getBytes()); + // Verify the signature with the public key. + return sig2.verify(realSig); } private static void printKeyInfo(KeyInfo keyInfo) { @@ -657,6 +748,15 @@ public class DRMTest { log("Algorithm: " + keyInfo.getAlgorithm()); log("Strength: " + keyInfo.getSize()); log("Status: " + keyInfo.getStatus()); + if (keyInfo.getPublicKey() != null) { + log("Public Key: "); + String publicKey = keyInfo.getPublicKey(); + int i = 0; + for (i = 0; i < publicKey.length() / 64; i++) { + log(publicKey.substring(i * 64, i * 64 + 64)); + } + log(publicKey.substring(i * 64)); + } } private static void log(String string) { diff --git a/base/kra/shared/conf/CS.cfg.in b/base/kra/shared/conf/CS.cfg.in index 5febae839..a3cf7918e 100644 --- a/base/kra/shared/conf/CS.cfg.in +++ b/base/kra/shared/conf/CS.cfg.in @@ -213,6 +213,9 @@ keys.ecc.curve.list=nistp256,nistp384,nistp521,sect163k1,nistk163,sect163r1,sect keys.ecc.curve.display.list=nistp256 (secp256r1),nistp384 (secp384r1),nistp521 (secp521r1),nistk163 (sect163k1),sect163r1,nistb163 (sect163r2),sect193r1,sect193r2,nistk233 (sect233k1),nistb233 (sect233r1),sect239k1,nistk283 (sect283k1),nistb283 (sect283r1),nistk409 (sect409k1),nistb409 (sect409r1),nistk571 (sect571k1),nistb571 (sect571r1),secp160k1,secp160r1,secp160r2,secp192k1,nistp192 (secp192r1, prime192v1),secp224k1,nistp224 (secp224r1),secp256k1,prime192v2,prime192v3,prime239v1,prime239v2,prime239v3,c2pnb163v1,c2pnb163v2,c2pnb163v3,c2pnb176v1,c2tnb191v1,c2tnb191v2,c2tnb191v3,c2pnb208w1,c2tnb239v1,c2tnb239v2,c2tnb239v3,c2pnb272w1,c2pnb304w1,c2tnb359w1,c2pnb368w1,c2tnb431r1,secp112r1,secp112r2,secp128r1,secp128r2,sect113r1,sect113r2,sect131r1,sect131r2 keys.ecc.curve.default=nistp256 keys.rsa.keysize.default=2048 +keys.rsa.min.size=256 +keys.rsa.max.size=8192 +keys.dsa.list=512,768,1024 internaldb._000=## internaldb._001=## Internal Database internaldb._002=## diff --git a/base/kra/src/com/netscape/kra/AsymKeyGenService.java b/base/kra/src/com/netscape/kra/AsymKeyGenService.java new file mode 100644 index 000000000..f4f68ea01 --- /dev/null +++ b/base/kra/src/com/netscape/kra/AsymKeyGenService.java @@ -0,0 +1,210 @@ +//--- BEGIN COPYRIGHT BLOCK --- +//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. +// +//(C) 2014 Red Hat, Inc. +//All rights reserved. +//--- END COPYRIGHT BLOCK --- +package com.netscape.kra; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; + +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.KeyPairAlgorithm; +import org.mozilla.jss.crypto.KeyPairGenerator; +import org.mozilla.jss.crypto.KeyPairGeneratorSpi; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.crypto.TokenException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.dbs.keydb.IKeyRecord; +import com.netscape.certsrv.dbs.keydb.IKeyRepository; +import com.netscape.certsrv.key.AsymKeyGenerationRequest; +import com.netscape.certsrv.key.KeyRequestResource; +import com.netscape.certsrv.kra.IKeyRecoveryAuthority; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.request.IService; +import com.netscape.certsrv.request.RequestId; +import com.netscape.certsrv.security.IStorageKeyUnit; +import com.netscape.cms.servlet.key.KeyRequestDAO; +import com.netscape.cmscore.dbs.KeyRecord; + +/** + * Service class to handle asymmetric key generation requests. + * A new asymmetric key is generated and archived the database as a key record. + * The private key is wrapped with the storage key and stored in the privateKeyData attribute of the + * ldap record. + * The public key is stored in the publicKeyData attribute of the record. + * + * @author akoneru + * + */ +public class AsymKeyGenService implements IService { + + private static final String ATTR_KEY_RECORD = "keyRecord"; + private static final String STATUS_ACTIVE = "active"; + + private IKeyRecoveryAuthority kra = null; + private IStorageKeyUnit storageUnit = null; + private ILogger signedAuditLogger = CMS.getSignedAuditLogger(); + private final static String LOGGING_SIGNED_AUDIT_ASYMKEY_GEN_REQUEST_PROCESSED = + "LOGGING_SIGNED_AUDIT_ASYMKEY_GEN_REQUEST_PROCESSED_6"; + + public AsymKeyGenService(IKeyRecoveryAuthority kra) { + this.kra = kra; + this.storageUnit = kra.getStorageKeyUnit(); + } + + @Override + public boolean serviceRequest(IRequest request) throws EBaseException { + + String clientKeyId = request.getExtDataInString(IRequest.SECURITY_DATA_CLIENT_KEY_ID); + String algorithm = request.getExtDataInString(IRequest.KEY_GEN_ALGORITHM); + + String keySizeStr = request.getExtDataInString(IRequest.KEY_GEN_SIZE); + int keySize = Integer.valueOf(keySizeStr); + + KeyPairGeneratorSpi.Usage[] usageList = null; + String usageStr = request.getExtDataInString(IRequest.KEY_GEN_USAGES); + if (usageStr != null) { + String[] usages = usageStr.split(","); + + if (usages.length > 0) { + usageList = new KeyPairGeneratorSpi.Usage[usages.length]; + for (int i = 0; i < usages.length; i++) { + switch (usages[i]) { + case AsymKeyGenerationRequest.DECRYPT: + usageList[i] = KeyPairGeneratorSpi.Usage.DECRYPT; + break; + case AsymKeyGenerationRequest.ENCRYPT: + usageList[i] = KeyPairGeneratorSpi.Usage.ENCRYPT; + break; + case AsymKeyGenerationRequest.WRAP: + usageList[i] = KeyPairGeneratorSpi.Usage.WRAP; + break; + case AsymKeyGenerationRequest.UNWRAP: + usageList[i] = KeyPairGeneratorSpi.Usage.UNWRAP; + break; + case AsymKeyGenerationRequest.DERIVE: + usageList[i] = KeyPairGeneratorSpi.Usage.DERIVE; + break; + case AsymKeyGenerationRequest.SIGN: + usageList[i] = KeyPairGeneratorSpi.Usage.SIGN; + break; + case AsymKeyGenerationRequest.SIGN_RECOVER: + usageList[i] = KeyPairGeneratorSpi.Usage.SIGN_RECOVER; + break; + case AsymKeyGenerationRequest.VERIFY: + usageList[i] = KeyPairGeneratorSpi.Usage.VERIFY; + break; + case AsymKeyGenerationRequest.VERIFY_RECOVER: + usageList[i] = KeyPairGeneratorSpi.Usage.VERIFY_RECOVER; + break; + } + } + } else { + usageList = new KeyPairGeneratorSpi.Usage[2]; + usageList[0] = KeyPairGeneratorSpi.Usage.DECRYPT; + usageList[1] = KeyPairGeneratorSpi.Usage.ENCRYPT; + } + } + + CMS.debug("AsymKeyGenService.serviceRequest. Request id: " + request.getRequestId()); + CMS.debug("AsymKeyGenService.serviceRequest algorithm: " + algorithm); + + KeyPairAlgorithm keyPairAlgorithm = KeyRequestDAO.ASYMKEY_GEN_ALGORITHMS.get(algorithm.toUpperCase()); + + String owner = request.getExtDataInString(IRequest.ATTR_REQUEST_OWNER); + String auditSubjectID = owner; + + // Get the token + CryptoToken token = kra.getKeygenToken(); + + // Generating the asymmetric keys + KeyPairGenerator keyPairGen = null; + KeyPair kp = null; + + try { + keyPairGen = token.getKeyPairGenerator(keyPairAlgorithm); + keyPairGen.initialize(keySize); + if (usageList != null) + keyPairGen.setKeyPairUsages(usageList, usageList); + kp = keyPairGen.genKeyPair(); + } catch (NoSuchAlgorithmException | TokenException e) { + CMS.debugStackTrace(); + auditAsymKeyGenRequestProcessed(auditSubjectID, ILogger.FAILURE, request.getRequestId(), + clientKeyId, null, "Failed to generate Asymmetric key"); + throw new EBaseException("Errors in generating Asymmetric key: " + e); + } + + KeyRecord record = new KeyRecord(null, kp.getPublic().getEncoded(), storageUnit.wrap((PrivateKey) kp + .getPrivate()), owner, algorithm, owner); + + IKeyRepository storage = kra.getKeyRepository(); + BigInteger serialNo = storage.getNextSerialNumber(); + + if (serialNo == null) { + kra.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_NEXT_SERIAL")); + auditAsymKeyGenRequestProcessed(auditSubjectID, ILogger.FAILURE, request.getRequestId(), + clientKeyId, null, "Failed to get next Key ID"); + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE")); + } + + // Storing the public key and private key. + record.set(IKeyRecord.ATTR_CLIENT_ID, clientKeyId); + record.setSerialNumber(serialNo); + record.set(KeyRecord.ATTR_ID, serialNo); + record.set(KeyRecord.ATTR_DATA_TYPE, KeyRequestResource.ASYMMETRIC_KEY_TYPE); + record.set(KeyRecord.ATTR_STATUS, STATUS_ACTIVE); + record.set(KeyRecord.ATTR_KEY_SIZE, keySize); + request.setExtData(ATTR_KEY_RECORD, serialNo); + + storage.addKeyRecord(record); + + auditAsymKeyGenRequestProcessed(auditSubjectID, ILogger.SUCCESS, request.getRequestId(), + clientKeyId, serialNo.toString(), "None"); + request.setExtData(IRequest.RESULT, IRequest.RES_SUCCESS); + kra.getRequestQueue().updateRequest(request); + return true; + } + + private void audit(String msg) { + if (signedAuditLogger == null) + return; + + signedAuditLogger.log(ILogger.EV_SIGNED_AUDIT, + null, + ILogger.S_SIGNED_AUDIT, + ILogger.LL_SECURITY, + msg); + } + + private void auditAsymKeyGenRequestProcessed(String subjectID, String status, RequestId requestID, + String clientKeyID, + String keyID, String reason) { + String auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_ASYMKEY_GEN_REQUEST_PROCESSED, + subjectID, + status, + requestID.toString(), + clientKeyID, + keyID != null ? keyID : "None", + reason); + audit(auditMessage); + } +} diff --git a/base/kra/src/com/netscape/kra/EncryptionUnit.java b/base/kra/src/com/netscape/kra/EncryptionUnit.java index 71bd1d781..8eabe05ae 100644 --- a/base/kra/src/com/netscape/kra/EncryptionUnit.java +++ b/base/kra/src/com/netscape/kra/EncryptionUnit.java @@ -43,6 +43,7 @@ import org.mozilla.jss.crypto.TokenException; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.key.KeyRequestResource; import com.netscape.certsrv.logging.ILogger; import com.netscape.certsrv.security.IEncryptionUnit; import com.netscape.cmscore.util.Debug; @@ -600,6 +601,7 @@ public abstract class EncryptionUnit implements IEncryptionUnit { pubKey, boolean temporary) throws EBaseException { try { + DerValue val = new DerValue(wrappedKeyData); // val.tag == DerValue.tag_Sequence DerInputStream in = val.data; @@ -623,13 +625,23 @@ public abstract class EncryptionUnit implements IEncryptionUnit { wrapper.initUnwrap(sk, IV); + // Get the key type for unwrapping the private key. + PrivateKey.Type keyType = null; + if (pubKey.getAlgorithm().equalsIgnoreCase(KeyRequestResource.RSA_ALGORITHM)) { + keyType = PrivateKey.RSA; + } else if (pubKey.getAlgorithm().equalsIgnoreCase(KeyRequestResource.DSA_ALGORITHM)) { + keyType = PrivateKey.DSA; + } else if (pubKey.getAlgorithm().equalsIgnoreCase(KeyRequestResource.EC_ALGORITHM)) { + keyType = PrivateKey.EC; + } + PrivateKey pk = null; if (temporary) { pk = wrapper.unwrapTemporaryPrivate(pri, - PrivateKey.RSA, pubKey); + keyType, pubKey); } else { pk = wrapper.unwrapPrivate(pri, - PrivateKey.RSA, pubKey); + keyType, pubKey); } return pk; } catch (TokenException e) { diff --git a/base/kra/src/com/netscape/kra/EnrollmentService.java b/base/kra/src/com/netscape/kra/EnrollmentService.java index 3ce37d6ae..d1b716cf8 100644 --- a/base/kra/src/com/netscape/kra/EnrollmentService.java +++ b/base/kra/src/com/netscape/kra/EnrollmentService.java @@ -272,7 +272,7 @@ public class EnrollmentService implements IService { } */ - // retrieve pubic key + // retrieve public key X509Key publicKey = getPublicKey(request, aOpts[i].mReqPos); byte publicKeyData[] = publicKey.getEncoded(); @@ -458,7 +458,7 @@ public class EnrollmentService implements IService { rec.setKeySize(-1); } - // if record alreay has a serial number, yell out. + // if record already has a serial number, yell out. if (rec.getSerialNumber() != null) { mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_INVALID_SERIAL_NUMBER", diff --git a/base/kra/src/com/netscape/kra/KRAService.java b/base/kra/src/com/netscape/kra/KRAService.java index f4768bd00..06c8ce1d5 100644 --- a/base/kra/src/com/netscape/kra/KRAService.java +++ b/base/kra/src/com/netscape/kra/KRAService.java @@ -50,6 +50,7 @@ public class KRAService implements IService { public final static String SECURITY_DATA_ENROLLMENT = IRequest.SECURITY_DATA_ENROLLMENT_REQUEST; public final static String SECURITY_DATA_RECOVERY = IRequest.SECURITY_DATA_RECOVERY_REQUEST; public final static String SYMKEY_GENERATION = IRequest.SYMKEY_GENERATION_REQUEST; + public final static String ASYMKEY_GENERATION = IRequest.ASYMKEY_GENERATION_REQUEST; // private variables @@ -68,6 +69,7 @@ public class KRAService implements IService { mServices.put(SECURITY_DATA_ENROLLMENT, new SecurityDataService(kra)); mServices.put(SECURITY_DATA_RECOVERY, new SecurityDataRecoveryService(kra)); mServices.put(SYMKEY_GENERATION, new SymKeyGenService(kra)); + mServices.put(ASYMKEY_GENERATION, new AsymKeyGenService(kra)); } /** diff --git a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java index a2d587318..752c8dff5 100644 --- a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java +++ b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java @@ -30,6 +30,9 @@ import java.util.Random; import javax.crypto.spec.RC2ParameterSpec; +import netscape.security.util.DerValue; +import netscape.security.x509.X509Key; + import org.dogtagpki.server.kra.rest.KeyRequestService; import org.mozilla.jss.CryptoManager; import org.mozilla.jss.asn1.OCTET_STRING; @@ -42,6 +45,7 @@ import org.mozilla.jss.crypto.KeyWrapAlgorithm; import org.mozilla.jss.crypto.KeyWrapper; import org.mozilla.jss.crypto.PBEAlgorithm; import org.mozilla.jss.crypto.PBEKeyGenParams; +import org.mozilla.jss.crypto.PrivateKey; import org.mozilla.jss.crypto.SymmetricKey; import org.mozilla.jss.crypto.TokenException; import org.mozilla.jss.pkcs12.PasswordConverter; @@ -123,36 +127,29 @@ public class SecurityDataRecoveryService implements IService { Hashtable<String, Object> params = mKRA.getVolatileRequest( request.getRequestId()); - BigInteger serialno = request.getExtDataInBigInteger(ATTR_SERIALNO); request.setExtData(ATTR_KEY_RECORD, serialno); RequestId requestID = request.getRequestId(); - if (params == null) { CMS.debug("Can't get volatile params."); auditRecoveryRequestProcessed(auditSubjectID, ILogger.FAILURE, requestID, serialno.toString(), "cannot get volatile params"); throw new EBaseException("Can't obtain volatile params!"); } - byte[] wrappedPassPhrase = null; byte[] wrappedSessKey = null; - String transWrappedSessKeyStr = (String) params.get(IRequest.SECURITY_DATA_TRANS_SESS_KEY); if (transWrappedSessKeyStr != null) { wrappedSessKey = Utils.base64decode(transWrappedSessKeyStr); } - String sessWrappedPassPhraseStr = (String) params.get(IRequest.SECURITY_DATA_SESS_PASS_PHRASE); if (sessWrappedPassPhraseStr != null) { wrappedPassPhrase = Utils.base64decode(sessWrappedPassPhraseStr); } - String ivInStr = (String) params.get(IRequest.SECURITY_DATA_IV_STRING_IN); if (ivInStr != null) { iv_in = Utils.base64decode(ivInStr); } - if (transWrappedSessKeyStr == null && sessWrappedPassPhraseStr == null) { //We may be in recovery case where no params were initially submitted. return false; @@ -167,46 +164,56 @@ public class SecurityDataRecoveryService implements IService { } catch (Exception e) { iv = iv_default; } - String ivStr = Utils.base64encode(iv); KeyRecord keyRecord = (KeyRecord) mStorage.readKeyRecord(serialno); SymmetricKey unwrappedSess = null; - String dataType = (String) keyRecord.get(IKeyRecord.ATTR_DATA_TYPE); SymmetricKey symKey = null; byte[] unwrappedSecData = null; + PrivateKey privateKey = null; if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) { symKey = recoverSymKey(keyRecord); + } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) { unwrappedSecData = recoverSecurityData(keyRecord); + } else if (dataType.equals(KeyRequestResource.ASYMMETRIC_KEY_TYPE)) { + try { + privateKey = mStorageUnit.unwrap(keyRecord.getPrivateKeyData(), + X509Key.parsePublicKey(new DerValue(keyRecord.getPublicKeyData()))); + } catch (IOException e) { + e.printStackTrace(); + CMS.debug("Cannot unwrap stored private key."); + throw new EBaseException("Cannot fetch the private key from the database."); + } + } else { + throw new EBaseException("Invalid data type stored in the database."); } - CryptoToken ct = mTransportUnit.getToken(); byte[] key_data = null; String pbeWrappedData = null; - if (sessWrappedPassPhraseStr != null) { //We have a trans wrapped pass phrase, we will be doing PBE packaging byte[] unwrappedPass = null; Password pass = null; - try { unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.DECRYPT); Cipher decryptor = ct.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); decryptor.initDecrypt(unwrappedSess, new IVParameterSpec(iv_in)); unwrappedPass = decryptor.doFinal(wrappedPassPhrase); String passStr = new String(unwrappedPass, "UTF-8"); - pass = new Password(passStr.toCharArray()); passStr = null; if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) { - pbeWrappedData = createEncryptedContentInfo(ct, symKey, null, + pbeWrappedData = createEncryptedContentInfo(ct, symKey, null, null, + pass); + } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)){ + pbeWrappedData = createEncryptedContentInfo(ct, null, unwrappedSecData, null, pass); - } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) { - pbeWrappedData = createEncryptedContentInfo(ct, null, unwrappedSecData, + } else if (dataType.equals(KeyRequestResource.ASYMMETRIC_KEY_TYPE)) { + pbeWrappedData = createEncryptedContentInfo(ct, null, null, privateKey, pass); } @@ -258,6 +265,19 @@ public class SecurityDataRecoveryService implements IService { serialno.toString(), "Cannot wrap pass phrase"); throw new EBaseException("Can't wrap pass phrase!"); } + + } else if (dataType.equals(KeyRequestResource.ASYMMETRIC_KEY_TYPE)) { + CMS.debug("Wrapping the private key with the session key"); + try { + unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.WRAP); + KeyWrapper wrapper = ct.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + wrapper.initWrap(unwrappedSess, new IVParameterSpec(iv)); + key_data = wrapper.wrap(privateKey); + } catch (Exception e) { + auditRecoveryRequestProcessed(auditSubjectID, ILogger.FAILURE, requestID, serialno.toString(), + "Cannot wrap private key"); + throw new EBaseException("Cannot wrap private key - " + e.toString()); + } } String wrappedKeyData = Utils.base64encode(key_data); @@ -319,10 +339,10 @@ public class SecurityDataRecoveryService implements IService { //ToDo: This might fit in JSS. private static EncryptedContentInfo - createEncryptedContentInfoPBEOfSymmKey(PBEAlgorithm keyGenAlg, Password password, byte[] salt, + createEncryptedContentInfoPBEOfKey(PBEAlgorithm keyGenAlg, Password password, byte[] salt, int iterationCount, KeyGenerator.CharToByteConverter charToByteConverter, - SymmetricKey symKey, CryptoToken token) + SymmetricKey symKey, PrivateKey privateKey, CryptoToken token) throws CryptoManager.NotInitializedException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, TokenException, CharConversionException { @@ -354,8 +374,15 @@ public class SecurityDataRecoveryService implements IService { KeyWrapper wrapper = token.getKeyWrapper( KeyWrapAlgorithm.DES3_CBC_PAD); wrapper.initWrap(key, params); - byte encrypted[] = wrapper.wrap(symKey); - + byte[] encrypted = null; + if (symKey != null) { + encrypted = wrapper.wrap(symKey); + } else if (privateKey != null) { + encrypted = wrapper.wrap(privateKey); + } + if (encrypted == null) { + //TODO - think about the exception to be thrown + } PBEParameter pbeParam = new PBEParameter(salt, iterationCount); AlgorithmIdentifier encAlgID = new AlgorithmIdentifier( keyGenAlg.toOID(), pbeParam); @@ -369,7 +396,7 @@ public class SecurityDataRecoveryService implements IService { } - private static String createEncryptedContentInfo(CryptoToken ct, SymmetricKey symKey, byte[] securityData, + private static String createEncryptedContentInfo(CryptoToken ct, SymmetricKey symKey, byte[] securityData, PrivateKey privateKey, Password password) throws EBaseException { @@ -384,14 +411,19 @@ public class SecurityDataRecoveryService implements IService { byte salt[] = { 0x01, 0x01, 0x01, 0x01 }; if (symKey != null) { - cInfo = createEncryptedContentInfoPBEOfSymmKey(keyGenAlg, password, salt, + cInfo = createEncryptedContentInfoPBEOfKey(keyGenAlg, password, salt, 1, passConverter, - symKey, ct); + symKey, null, ct); } else if (securityData != null) { cInfo = EncryptedContentInfo.createPBE(keyGenAlg, password, salt, 1, passConverter, securityData); + } else if (privateKey != null) { + cInfo = createEncryptedContentInfoPBEOfKey(keyGenAlg, password, salt, + 1, + passConverter, + null, privateKey, ct); } if(cInfo == null) { diff --git a/base/kra/src/com/netscape/kra/SecurityDataService.java b/base/kra/src/com/netscape/kra/SecurityDataService.java index 4a2ebef34..25bb240e1 100644 --- a/base/kra/src/com/netscape/kra/SecurityDataService.java +++ b/base/kra/src/com/netscape/kra/SecurityDataService.java @@ -182,9 +182,12 @@ public class SecurityDataService implements IService { // create key record // Note that in this case the owner is the same as the approving agent // because the archival request is made by the agent. + // The algorithm used to generate the symmetric key (being stored as the secret) + // is set in later in this method. (which is different from the algStr variable + // which is the algorithm used for encrypting the secret.) KeyRecord rec = new KeyRecord(null, publicKey, privateSecurityData, owner, - algStr, owner); + null, owner); rec.set(IKeyRecord.ATTR_CLIENT_ID, clientKeyId); diff --git a/base/kra/src/com/netscape/kra/SymKeyGenService.java b/base/kra/src/com/netscape/kra/SymKeyGenService.java index 46c8265f0..d308345d7 100644 --- a/base/kra/src/com/netscape/kra/SymKeyGenService.java +++ b/base/kra/src/com/netscape/kra/SymKeyGenService.java @@ -88,13 +88,13 @@ public class SymKeyGenService implements IService { throws EBaseException { String id = request.getRequestId().toString(); String clientKeyId = request.getExtDataInString(IRequest.SECURITY_DATA_CLIENT_KEY_ID); - String algorithm = request.getExtDataInString(IRequest.SYMKEY_GEN_ALGORITHM); + String algorithm = request.getExtDataInString(IRequest.KEY_GEN_ALGORITHM); - String usageStr = request.getExtDataInString(IRequest.SYMKEY_GEN_USAGES); + String usageStr = request.getExtDataInString(IRequest.KEY_GEN_USAGES); List<String> usages = new ArrayList<String>( Arrays.asList(StringUtils.split(usageStr, ","))); - String keySizeStr = request.getExtDataInString(IRequest.SYMKEY_GEN_SIZE); + String keySizeStr = request.getExtDataInString(IRequest.KEY_GEN_SIZE); int keySize = Integer.parseInt(keySizeStr); CMS.debug("SymKeyGenService.serviceRequest. Request id: " + id); @@ -111,7 +111,7 @@ public class SymKeyGenService implements IService { } CryptoToken token = mStorageUnit.getToken(); - KeyGenAlgorithm kgAlg = KeyRequestDAO.KEYGEN_ALGORITHMS.get(algorithm); + KeyGenAlgorithm kgAlg = KeyRequestDAO.SYMKEY_GEN_ALGORITHMS.get(algorithm); if (kgAlg == null) { throw new EBaseException("Invalid algorithm"); } @@ -209,7 +209,6 @@ public class SymKeyGenService implements IService { rec.set(KeyRecord.ATTR_ID, serialNo); rec.set(KeyRecord.ATTR_DATA_TYPE, KeyRequestResource.SYMMETRIC_KEY_TYPE); rec.set(KeyRecord.ATTR_STATUS, STATUS_ACTIVE); - rec.set(KeyRecord.ATTR_ALGORITHM, algorithm); rec.set(KeyRecord.ATTR_KEY_SIZE, keySize); request.setExtData(ATTR_KEY_RECORD, serialNo); diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java b/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java index c538e016b..04dd3253f 100644 --- a/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java +++ b/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java @@ -45,6 +45,7 @@ import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.base.ResourceMessage; import com.netscape.certsrv.base.UnauthorizedException; import com.netscape.certsrv.dbs.keydb.KeyId; +import com.netscape.certsrv.key.AsymKeyGenerationRequest; import com.netscape.certsrv.key.KeyArchivalRequest; import com.netscape.certsrv.key.KeyRecoveryRequest; import com.netscape.certsrv.key.KeyRequestInfo; @@ -88,6 +89,9 @@ public class KeyRequestService extends PKIService implements KeyRequestResource private static final String LOGGING_SIGNED_AUDIT_SYMKEY_GENERATION_REQUEST = "LOGGING_SIGNED_AUDIT_SYMKEY_GENERATION_REQUEST_4"; + private static final String LOGGING_SIGNED_AUDIT_ASYMKEY_GENERATION_REQUEST = + "LOGGING_SIGNED_AUDIT_ASYMKEY_GENERATION_REQUEST_4"; + private static final String LOGGING_SIGNED_AUDIT_SECURITY_DATA_RECOVERY_REQUEST = "LOGGING_SIGNED_AUDIT_SECURITY_DATA_RECOVERY_REQUEST_4"; @@ -412,14 +416,24 @@ public class KeyRequestService extends PKIService implements KeyRequestResource auditor.log(msg); } + public void auditAsymKeyGenRequestMade(RequestId requestId, String status, String clientKeyID) { + String msg = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_ASYMKEY_GENERATION_REQUEST, + servletRequest.getUserPrincipal().getName(), + status, + requestId != null ? requestId.toString() : "null", + clientKeyID); + auditor.log(msg); + } + @Override - public Response createRequest(MultivaluedMap<String, String> form) { + public Response submitRequest(MultivaluedMap<String, String> form) { ResourceMessage data = new ResourceMessage(form); - return createRequest(data); + return submitRequest(data); } @Override - public Response createRequest(ResourceMessage data) { + public Response submitRequest(ResourceMessage data) { Object request = null; try { Class<?> requestClazz = Class.forName(data.getClassName()); @@ -435,6 +449,8 @@ public class KeyRequestService extends PKIService implements KeyRequestResource return recoverKey(new KeyRecoveryRequest(data)); } else if (request instanceof SymKeyGenerationRequest) { return generateSymKey(new SymKeyGenerationRequest(data)); + } else if (request instanceof AsymKeyGenerationRequest) { + return generateAsymKey(new AsymKeyGenerationRequest(data)); } else { throw new BadRequestException("Invalid request class."); } @@ -464,4 +480,26 @@ public class KeyRequestService extends PKIService implements KeyRequestResource throw new PKIException(e.toString()); } } + + public Response generateAsymKey(AsymKeyGenerationRequest data) { + if (data == null) { + throw new BadRequestException("Invalid key generation request."); + } + + KeyRequestDAO dao = new KeyRequestDAO(); + KeyRequestResponse response; + try { + String owner = servletRequest.getUserPrincipal().getName(); + response = dao.submitRequest(data, uriInfo, owner); + auditAsymKeyGenRequestMade(response.getRequestInfo().getRequestId(), ILogger.SUCCESS, + data.getClientKeyId()); + + return createCreatedResponse(response, new URI(response.getRequestInfo().getRequestURL())); + + } catch (EBaseException | URISyntaxException e) { + e.printStackTrace(); + auditArchivalRequestMade(null, ILogger.FAILURE, data.getClientKeyId()); + throw new PKIException(e.toString()); + } + } } diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java b/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java index 9f33b1ba7..ecf3b0398 100644 --- a/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java +++ b/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java @@ -19,6 +19,7 @@ package org.dogtagpki.server.kra.rest; +import java.io.IOException; import java.math.BigInteger; import java.net.URI; import java.util.ArrayList; @@ -69,6 +70,7 @@ import com.netscape.certsrv.request.RequestId; import com.netscape.certsrv.request.RequestStatus; import com.netscape.cms.servlet.base.PKIService; import com.netscape.cms.servlet.key.KeyRequestDAO; +import com.netscape.cmsutil.crypto.CryptoUtil; import com.netscape.cmsutil.ldap.LDAPUtil; import com.netscape.cmsutil.util.Utils; @@ -376,7 +378,7 @@ public class KeyService extends PKIService implements KeyResource { while (e.hasMoreElements()) { IKeyRecord rec = e.nextElement(); if (rec == null) continue; - results.add(createKeyDataInfo(rec)); + results.add(createKeyDataInfo(rec, false)); } int total = results.size(); @@ -431,13 +433,20 @@ public class KeyService extends PKIService implements KeyResource { throw new ResourceNotFoundException("Key not found."); } - public KeyInfo createKeyDataInfo(IKeyRecord rec) throws EBaseException { + public KeyInfo createKeyDataInfo(IKeyRecord rec, boolean getPublicKey) throws EBaseException { KeyInfo ret = new KeyInfo(); ret.setClientKeyID(rec.getClientId()); ret.setStatus(rec.getKeyStatus()); ret.setAlgorithm(rec.getAlgorithm()); ret.setSize(rec.getKeySize()); ret.setOwnerName(rec.getOwnerName()); + if(rec.getPublicKeyData() != null && getPublicKey){ + try { + ret.setPublicKey(CryptoUtil.base64Encode(rec.getPublicKeyData())); + } catch (IOException e) { + throw new EBaseException(e.getMessage()); + } + } Path keyPath = KeyResource.class.getAnnotation(Path.class); BigInteger serial = rec.getSerialNumber(); @@ -539,7 +548,7 @@ public class KeyService extends PKIService implements KeyResource { IKeyRecord rec = null; try { rec = repo.readKeyRecord(keyId.toBigInteger()); - KeyInfo info = createKeyDataInfo(rec); + KeyInfo info = createKeyDataInfo(rec, true); return createOKResponse(info); } catch (EDBRecordNotFoundException e) { 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 dd0393aab..8c014b02d 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 @@ -17,6 +17,7 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cms.servlet.key; +import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; @@ -30,14 +31,17 @@ import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.StringUtils; import org.mozilla.jss.crypto.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyPairAlgorithm; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.BadRequestException; import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.PKIException; 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.AsymKeyGenerationRequest; import com.netscape.certsrv.key.KeyArchivalRequest; import com.netscape.certsrv.key.KeyData; import com.netscape.certsrv.key.KeyNotFoundException; @@ -63,16 +67,21 @@ import com.netscape.cms.servlet.request.CMSRequestDAO; */ public class KeyRequestDAO extends CMSRequestDAO { - public static final Map<String, KeyGenAlgorithm> KEYGEN_ALGORITHMS; + public static final Map<String, KeyGenAlgorithm> SYMKEY_GEN_ALGORITHMS; + public static final Map<String, KeyPairAlgorithm> ASYMKEY_GEN_ALGORITHMS; static { - KEYGEN_ALGORITHMS = new HashMap<String, KeyGenAlgorithm>(); - KEYGEN_ALGORITHMS.put(KeyRequestResource.DES_ALGORITHM, KeyGenAlgorithm.DES); - KEYGEN_ALGORITHMS.put(KeyRequestResource.DESEDE_ALGORITHM, KeyGenAlgorithm.DESede); - KEYGEN_ALGORITHMS.put(KeyRequestResource.DES3_ALGORITHM, KeyGenAlgorithm.DES3); - KEYGEN_ALGORITHMS.put(KeyRequestResource.RC2_ALGORITHM, KeyGenAlgorithm.RC2); - KEYGEN_ALGORITHMS.put(KeyRequestResource.RC4_ALGORITHM, KeyGenAlgorithm.RC4); - KEYGEN_ALGORITHMS.put(KeyRequestResource.AES_ALGORITHM, KeyGenAlgorithm.AES); + SYMKEY_GEN_ALGORITHMS = new HashMap<String, KeyGenAlgorithm>(); + SYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.DES_ALGORITHM, KeyGenAlgorithm.DES); + SYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.DESEDE_ALGORITHM, KeyGenAlgorithm.DESede); + SYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.DES3_ALGORITHM, KeyGenAlgorithm.DES3); + SYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.RC2_ALGORITHM, KeyGenAlgorithm.RC2); + SYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.RC4_ALGORITHM, KeyGenAlgorithm.RC4); + SYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.AES_ALGORITHM, KeyGenAlgorithm.AES); + + ASYMKEY_GEN_ALGORITHMS = new HashMap<String, KeyPairAlgorithm>(); + ASYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.RSA_ALGORITHM, KeyPairAlgorithm.RSA); + ASYMKEY_GEN_ALGORITHMS.put(KeyRequestResource.DSA_ALGORITHM, KeyPairAlgorithm.DSA); } private static String REQUEST_ARCHIVE_OPTIONS = IEnrollProfile.REQUEST_ARCHIVE_OPTIONS; @@ -288,7 +297,7 @@ public class KeyRequestDAO extends CMSRequestDAO { keySize = new Integer(128); } - KeyGenAlgorithm alg = KEYGEN_ALGORITHMS.get(algName); + KeyGenAlgorithm alg = SYMKEY_GEN_ALGORITHMS.get(algName); if (alg == null) { throw new BadRequestException("Invalid Algorithm"); } @@ -299,17 +308,98 @@ public class KeyRequestDAO extends CMSRequestDAO { IRequest request = queue.newRequest(IRequest.SYMKEY_GENERATION_REQUEST); - request.setExtData(IRequest.SYMKEY_GEN_ALGORITHM, algName); - request.setExtData(IRequest.SYMKEY_GEN_SIZE, keySize); + request.setExtData(IRequest.KEY_GEN_ALGORITHM, algName); + request.setExtData(IRequest.KEY_GEN_SIZE, keySize); request.setExtData(IRequest.SECURITY_DATA_STRENGTH, keySize); request.setExtData(IRequest.SECURITY_DATA_ALGORITHM, algName); - request.setExtData(IRequest.SYMKEY_GEN_USAGES, StringUtils.join(usages, ",")); + request.setExtData(IRequest.KEY_GEN_USAGES, StringUtils.join(usages, ",")); request.setExtData(IRequest.SECURITY_DATA_CLIENT_KEY_ID, clientKeyId); request.setExtData(IRequest.ATTR_REQUEST_OWNER, owner); if (transWrappedSessionKey != null) { - request.setExtData(IRequest.SYMKEY_TRANS_WRAPPED_SESSION_KEY, + request.setExtData(IRequest.KEY_GEN_TRANS_WRAPPED_SESSION_KEY, + transWrappedSessionKey); + } + + queue.processRequest(request); + queue.markAsServiced(request); + + return createKeyRequestResponse(request, uriInfo); + } + + public KeyRequestResponse submitRequest(AsymKeyGenerationRequest data, UriInfo uriInfo, String owner) + throws EBaseException { + String clientKeyId = data.getClientKeyId(); + 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"); + if (keyExists == true) { + throw new BadRequestException("Cannot archive already active existing key!"); + } + + if (StringUtils.isBlank(algName)) { + if (keySize.intValue() != 0) { + throw new BadRequestException( + "Invalid request. Must specify key algorithm if size is specified"); + } + } + + KeyPairAlgorithm alg = ASYMKEY_GEN_ALGORITHMS.get(algName); + if (alg == null) { + throw new BadRequestException("Unsupported algorithm specified."); + } + + if (keySize == null) { + if (algName.equalsIgnoreCase(KeyRequestResource.RSA_ALGORITHM) + || algName.equalsIgnoreCase(KeyRequestResource.DSA_ALGORITHM)) { + throw new BadRequestException("Key size must be specified."); + } + } else { + //Validate key size + if (algName.equalsIgnoreCase(KeyRequestResource.RSA_ALGORITHM)) { + int size = Integer.valueOf(keySize); + int minSize = Integer.valueOf(CMS.getConfigStore().getInteger("keys.rsa.min.size", 256)); + int maxSize = Integer.valueOf(CMS.getConfigStore().getInteger("keys.rsa.max.size", 8192)); + if (minSize > maxSize) { + throw new PKIException("Incorrect size parameters stored in config file."); + } + if (size < minSize || size > maxSize) { + throw new BadRequestException("Key size out of supported range - " + minSize + " - " + maxSize); + } + //JSS supports key sizes that are of the form 256 + (16*n), where n = 0-1008, for RSA + if (((size - 256) % 16) != 0) { + throw new BadRequestException("Invalid key size specified."); + } + } else if (algName.equalsIgnoreCase(KeyRequestResource.DSA_ALGORITHM)) { + // Without the PQGParams, JSS can create DSA keys of size 512, 768, 1024 only. + String[] sizes = CMS.getConfigStore().getString("keys.dsa.list", "512,768,1024").split(","); + if (!Arrays.asList(sizes).contains(String.valueOf(keySize))) { + throw new BadRequestException("Invalid key size specified."); + } + } + } + + IRequest request = queue.newRequest(IRequest.ASYMKEY_GENERATION_REQUEST); + + request.setExtData(IRequest.KEY_GEN_ALGORITHM, algName); + request.setExtData(IRequest.KEY_GEN_SIZE, keySize); + request.setExtData(IRequest.SECURITY_DATA_STRENGTH, keySize); + request.setExtData(IRequest.SECURITY_DATA_ALGORITHM, algName); + + request.setExtData(IRequest.KEY_GEN_USAGES, StringUtils.join(usages, ",")); + request.setExtData(IRequest.SECURITY_DATA_CLIENT_KEY_ID, clientKeyId); + request.setExtData(IRequest.ATTR_REQUEST_OWNER, owner); + + if (transWrappedSessionKey != null) { + request.setExtData(IRequest.KEY_GEN_TRANS_WRAPPED_SESSION_KEY, transWrappedSessionKey); } diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties index f1485deec..dfa23c15b 100644 --- a/base/server/cmsbundle/src/LogMessages.properties +++ b/base/server/cmsbundle/src/LogMessages.properties @@ -2448,6 +2448,15 @@ LOGGING_SIGNED_AUDIT_SYMKEY_GEN_REQUEST_PROCESSED_6=<type=SYMKEY_GENERATION_REQU # LOGGING_SIGNED_AUDIT_SYMKEY_GENERATION_REQUEST_4=<type=SYMKEY_GENERATION_REQUEST>:[AuditEvent=SYMKEY_GENERATION_REQUEST][SubjectID={0}][Outcome={1}][GenerationRequestID={2}][ClientKeyID={3}] symkey generation request made # +# LOGGING_SIGNED_AUDIT_ASYMKEY_GENERATION_REQUEST +# - used when asymmetric key generation request is made +LOGGING_SIGNED_AUDIT_ASYMKEY_GENERATION_REQUEST_4=<type=ASYMKEY_GENERATION_REQUEST>:[AuditEvent=ASYMKEY_GENERATION_REQUEST][SubjectID={0}][Outcome={1}][GenerationRequestID={2}][ClientKeyID={3}] Asymkey generation request made +# +# LOGGING_SIGNED_AUDIT_ASYMKEY_GEN_REQUEST_PROCESSED +# - used when a request to generate asymmetric keys received by the DRM +# is processed. +LOGGING_SIGNED_AUDIT_ASYMKEY_GEN_REQUEST_PROCESSED_6=<type=ASYMKEY_GENERATION_REQUEST_PROCESSED>:[AuditEvent=ASYMKEY_GENERATION_REQUEST_PROCESSED][SubjectID={0}][Outcome={1}][GenerationRequestID={2}][ClientKeyID={3}][KeyID={4}][FailureReason={5}] Asymkey generation request processed +# # LOGGING_SIGNED_AUDIT_TOKEN_CERT_ENROLLMENT # - used for TPS when token certificate enrollment request is made # |