diff options
5 files changed, 437 insertions, 11 deletions
diff --git a/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java b/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java index 1abaaab00..19e6aa67c 100644 --- a/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java +++ b/base/common/src/com/netscape/certsrv/key/SymKeyGenerationRequest.java @@ -1,10 +1,16 @@ 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; + /** * @author alee * @@ -14,7 +20,38 @@ import javax.xml.bind.annotation.XmlRootElement; public class SymKeyGenerationRequest extends KeyRequest { private static final String CLIENT_ID = "clientID"; - private static final String DATA_TYPE = "dataType"; + private static final String KEY_SIZE = "keySize"; + private static final String KEY_ALGORITHM = "keyAlgorithm"; + private static final String KEY_USAGE = "keyUsage"; + + // usages + public static final String ENCRYPT_USAGE = "encrypt"; + public static final String DECRYPT_USAGE = "decrypt"; + public static final String SIGN_USAGE = "sign"; + public static final String VERIFY_USAGE = "verify"; + public static final String WRAP_USAGE = "wrap"; + public static final String UWRAP_USAGE = "unwrap"; + + public List<String> getUsages() { + String usageString = properties.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) { + this.properties.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) @@ -22,7 +59,14 @@ public class SymKeyGenerationRequest extends KeyRequest { public SymKeyGenerationRequest(MultivaluedMap<String, String> form) { this.properties.put(CLIENT_ID, form.getFirst(CLIENT_ID)); - this.properties.put(DATA_TYPE, form.getFirst(DATA_TYPE)); + this.properties.put(KEY_SIZE, form.getFirst(KEY_SIZE)); + this.properties.put(KEY_ALGORITHM, form.getFirst(KEY_ALGORITHM)); + this.properties.put(KEY_USAGE, form.getFirst(KEY_USAGE)); + + String usageString = properties.get(KEY_USAGE); + if (! StringUtils.isBlank(usageString)) { + setUsages(new ArrayList<String>(Arrays.asList(usageString.split(",")))); + } } /** @@ -40,17 +84,31 @@ public class SymKeyGenerationRequest extends KeyRequest { } /** - * @return the dataType + * @return the keySize + */ + public int getKeySize() { + return Integer.parseInt(this.properties.get(KEY_SIZE)); + } + + /** + * @param keySize the key size to set + */ + public void setKeySize(int keySize) { + this.properties.put(KEY_SIZE, Integer.toString(keySize)); + } + + /** + * @return the keyAlgorithm */ - public String getDataType() { - return this.properties.get(DATA_TYPE); + public String getKeyAlgorithm() { + return this.properties.get(KEY_ALGORITHM); } /** - * @param dataType the dataType to set + * @param keyAlgorithm the key algorithm to set */ - public void setDataType(String dataType) { - this.properties.put(DATA_TYPE, dataType); + public void setKeyAlgorithm(String keyAlgorithm) { + this.properties.put(KEY_ALGORITHM, keyAlgorithm); } public String toString() { @@ -73,8 +131,12 @@ public class SymKeyGenerationRequest extends KeyRequest { SymKeyGenerationRequest before = new SymKeyGenerationRequest(); before.setClientId("vek 12345"); - before.setDataType(KeyRequestResource.SYMMETRIC_KEY_TYPE); + before.setKeyAlgorithm("aes"); + before.setKeySize(128); before.setRequestType(KeyRequestResource.KEY_GENERATION_REQUEST); + before.addUsage(SymKeyGenerationRequest.DECRYPT_USAGE); + before.addUsage(SymKeyGenerationRequest.ENCRYPT_USAGE); + before.addUsage(SymKeyGenerationRequest.SIGN_USAGE); String string = before.toString(); System.out.println(string); diff --git a/base/common/src/com/netscape/certsrv/request/IRequest.java b/base/common/src/com/netscape/certsrv/request/IRequest.java index 60c083e6a..05908fc1d 100644 --- a/base/common/src/com/netscape/certsrv/request/IRequest.java +++ b/base/common/src/com/netscape/certsrv/request/IRequest.java @@ -167,6 +167,11 @@ 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 + 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"; // requestor type values. public static final String REQUESTOR_EE = "EE"; diff --git a/base/kra/src/com/netscape/kra/SymKeyGenService.java b/base/kra/src/com/netscape/kra/SymKeyGenService.java new file mode 100644 index 000000000..c3a03d968 --- /dev/null +++ b/base/kra/src/com/netscape/kra/SymKeyGenService.java @@ -0,0 +1,280 @@ +// --- 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) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.kra; + +import java.io.CharConversionException; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyGenerator; +import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.TokenException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.SessionContext; +import com.netscape.certsrv.dbs.keydb.IKeyRecord; +import com.netscape.certsrv.dbs.keydb.IKeyRepository; +import com.netscape.certsrv.key.SymKeyGenerationRequest; +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.cmscore.dbs.KeyRecord; + +/** + * This implementation implements SecurityData archival operations. + * <p> + * + * @version $Revision$, $Date$ + */ +public class SymKeyGenService implements IService { + + private final static String DEFAULT_OWNER = "IPA Agent"; + public final static String ATTR_KEY_RECORD = "keyRecord"; + private final static String STATUS_ACTIVE = "active"; + + private IKeyRecoveryAuthority mKRA = null; + private IStorageKeyUnit mStorageUnit = null; + private ILogger signedAuditLogger = CMS.getSignedAuditLogger(); + + private final static String LOGGING_SIGNED_AUDIT_SYMKEY_GEN_REQUEST_PROCESSED = + "LOGGING_SIGNED_AUDIT_SYMKEY_GEN_REQUEST_PROCESSED_6"; + + public SymKeyGenService(IKeyRecoveryAuthority kra) { + mKRA = kra; + mStorageUnit = kra.getStorageKeyUnit(); + } + + /** + * Performs the service of archiving Security Data. + * represented by this request. + * <p> + * + * @param request + * The request that needs service. The service may use + * attributes stored in the request, and may update the + * values, or store new ones. + * @return + * an indication of whether this request is still pending. + * 'false' means the request will wait for further notification. + * @exception EBaseException indicates major processing failure. + */ + public boolean serviceRequest(IRequest request) + throws EBaseException { + String id = request.getRequestId().toString(); + String clientId = request.getExtDataInString(IRequest.SECURITY_DATA_CLIENT_ID); + 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(","))); + + String keySizeStr = request.getExtDataInString(IRequest.SYMKEY_GEN_SIZE); + int keySize = Integer.parseInt(keySizeStr); + + CMS.debug("SymKeyGenService.serviceRequest. Request id: " + id); + CMS.debug("SymKeyGenService.serviceRequest algorithm: " + algorithm); + + String owner = getOwnerName(request); + String subjectID = auditSubjectID(); + + //Check here even though restful layer checks for this. + if (algorithm == null || clientId == null || keySize <= 0) { + auditSymKeyGenRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(), + clientId, null, "Bad data in request"); + throw new EBaseException("Bad data in SymKeyGenService.serviceRequest"); + } + + CryptoToken token = mStorageUnit.getToken(); + KeyGenAlgorithm kgAlg = getKeyGenAlgorithm(algorithm); + + SymmetricKey.Usage keyUsages[]; + if (usages.size() > 0) { + keyUsages = new SymmetricKey.Usage[usages.size()]; + int index = 0; + for (String usage : usages) { + switch (usage) { + case SymKeyGenerationRequest.DECRYPT_USAGE: + keyUsages[index] = SymmetricKey.Usage.DECRYPT; + break; + case SymKeyGenerationRequest.ENCRYPT_USAGE: + keyUsages[index] = SymmetricKey.Usage.ENCRYPT; + break; + case SymKeyGenerationRequest.WRAP_USAGE: + keyUsages[index] = SymmetricKey.Usage.WRAP; + break; + case SymKeyGenerationRequest.UWRAP_USAGE: + keyUsages[index] = SymmetricKey.Usage.UNWRAP; + break; + case SymKeyGenerationRequest.SIGN_USAGE: + keyUsages[index] = SymmetricKey.Usage.SIGN; + break; + case SymKeyGenerationRequest.VERIFY_USAGE: + keyUsages[index] = SymmetricKey.Usage.VERIFY; + break; + default: + throw new EBaseException("Invalid usage"); + } + index++; + } + } else { + keyUsages = new SymmetricKey.Usage[2]; + keyUsages[0] = SymmetricKey.Usage.DECRYPT; + keyUsages[1] = SymmetricKey.Usage.ENCRYPT; + } + + SymmetricKey sk = null; + try { + KeyGenerator kg = token.getKeyGenerator(kgAlg); + kg.setKeyUsages(keyUsages); + kg.temporaryKeys(true); + sk = kg.generate(); + CMS.debug("SymKeyGenService:wrap() session key generated on slot: " + token.getName()); + } catch (TokenException | IllegalStateException | CharConversionException | NoSuchAlgorithmException e) { + auditSymKeyGenRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(), + clientId, null, "Failed to generate symmetric key"); + throw new EBaseException("Errors in generating symmetric key: " + e); + } + + String keyType = null; + + byte[] publicKey = null; + byte privateSecurityData[] = null; + + if (sk != null) { + privateSecurityData = mStorageUnit.wrap(sk); + } else { // We have no data. + auditSymKeyGenRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(), + clientId, null, "Failed to create security data to archive"); + throw new EBaseException("Failed to create security data to archive!"); + } + + // create key record + KeyRecord rec = new KeyRecord(null, publicKey, + privateSecurityData, owner, + algorithm, owner); + + rec.set(IKeyRecord.ATTR_CLIENT_ID, clientId); + + //Now we need a serial number for our new key. + if (rec.getSerialNumber() != null) { + auditSymKeyGenRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(), + clientId, null, CMS.getUserMessage("CMS_KRA_INVALID_STATE")); + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE")); + } + + IKeyRepository storage = mKRA.getKeyRepository(); + BigInteger serialNo = storage.getNextSerialNumber(); + + if (serialNo == null) { + mKRA.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSCORE_KRA_GET_NEXT_SERIAL")); + auditSymKeyGenRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(), + clientId, null, "Failed to get next Key ID"); + throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE")); + } + + rec.set(KeyRecord.ATTR_ID, serialNo); + rec.set(KeyRecord.ATTR_DATA_TYPE, keyType); + rec.set(KeyRecord.ATTR_STATUS, STATUS_ACTIVE); + request.setExtData(ATTR_KEY_RECORD, serialNo); + + CMS.debug("KRA adding Security Data key record " + serialNo); + storage.addKeyRecord(rec); + + auditSymKeyGenRequestProcessed(subjectID, ILogger.SUCCESS, request.getRequestId(), + clientId, serialNo.toString(), "None"); + + return true; + } + + KeyGenAlgorithm getKeyGenAlgorithm(String algorithm) throws EBaseException { + switch (algorithm) { + case "DES": + return KeyGenAlgorithm.DES; + case "DESede": + return KeyGenAlgorithm.DESede; + case "DES3": + return KeyGenAlgorithm.DES3; + case "RC4": + return KeyGenAlgorithm.RC4; + case "AES": + return KeyGenAlgorithm.AES; + case "RC2": + return KeyGenAlgorithm.RC2; + default: + throw new EBaseException("Invalid algorithm"); + } + } + + //ToDo: return real owner with auth + private String getOwnerName(IRequest request) { + return DEFAULT_OWNER; + } + + 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 String auditSubjectID() { + if (signedAuditLogger == null) { + return null; + } + + String subjectID = null; + + // Initialize subjectID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + subjectID = (String) auditContext.get(SessionContext.USER_ID); + subjectID = (subjectID != null) ? subjectID.trim() : ILogger.NONROLEUSER; + } else { + subjectID = ILogger.UNIDENTIFIED; + } + + return subjectID; + } + + private void auditSymKeyGenRequestProcessed(String subjectID, String status, RequestId requestID, String clientID, + String keyID, String reason) { + String auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_SYMKEY_GEN_REQUEST_PROCESSED, + subjectID, + status, + requestID.toString(), + clientID, + keyID != null ? keyID : "None", + reason); + audit(auditMessage); + } +}
\ No newline at end of file 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 49cd4515d..a67bff754 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 @@ -26,7 +26,11 @@ import javax.ws.rs.Path; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; +import org.apache.commons.lang.StringUtils; +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.keydb.IKeyRecord; import com.netscape.certsrv.dbs.keydb.IKeyRepository; @@ -37,6 +41,7 @@ import com.netscape.certsrv.key.KeyRequestInfo; import com.netscape.certsrv.key.KeyRequestInfos; import com.netscape.certsrv.key.KeyRequestResource; import com.netscape.certsrv.key.KeyResource; +import com.netscape.certsrv.key.SymKeyGenerationRequest; import com.netscape.certsrv.kra.IKeyRecoveryAuthority; import com.netscape.certsrv.profile.IEnrollProfile; import com.netscape.certsrv.request.CMSRequestInfo; @@ -198,6 +203,62 @@ public class KeyRequestDAO extends CMSRequestDAO { return createKeyRequestInfo(request, uriInfo); } + public KeyRequestInfo submitRequest(SymKeyGenerationRequest data, UriInfo uriInfo) throws EBaseException { + String clientId = data.getClientId(); + String algName = data.getKeyAlgorithm(); + int size = data.getKeySize(); + List<String> usages = data.getUsages(); + + if (StringUtils.isBlank(clientId) || StringUtils.isBlank(algName) || (size<=0)) { + throw new BadRequestException("Invalid key generation request. Missing parameters"); + } + + boolean keyExists = doesKeyExist(clientId, "active", uriInfo); + if (keyExists == true) { + throw new BadRequestException("Can not archive already active existing key!"); + } + + boolean isValid = true; + switch(algName) { + case "DES": + if (! KeyGenAlgorithm.DES.isValidStrength(size)) isValid = false; + break; + case "DESede": + if (! KeyGenAlgorithm.DESede.isValidStrength(size)) isValid = false; + break; + case "DES3": + if (! KeyGenAlgorithm.DES3.isValidStrength(size)) isValid = false; + break; + case "RC4": + if (! KeyGenAlgorithm.RC4.isValidStrength(size)) isValid = false; + break; + case "AES": + if (! KeyGenAlgorithm.AES.isValidStrength(size)) isValid = false; + break; + case "RC2": + if (! KeyGenAlgorithm.RC2.isValidStrength(size)) isValid = false; + break; + default: + throw new BadRequestException("Invalid algorithm"); + } + + if (!isValid) { + throw new BadRequestException("Invalid key size for this algorithm"); + } + + IRequest request = queue.newRequest(IRequest.SYMKEY_GENERATION_REQUEST); + + request.setExtData(IRequest.SYMKEY_GEN_ALGORITHM, algName); + request.setExtData(IRequest.SYMKEY_GEN_SIZE, size); + request.setExtData(IRequest.SYMKEY_GEN_USAGES, StringUtils.join(usages, ",")); + request.setExtData(IRequest.SECURITY_DATA_CLIENT_ID, clientId); + + queue.processRequest(request); + queue.markAsServiced(request); + + return createKeyRequestInfo(request, uriInfo); + } + public void approveRequest(RequestId id) throws EBaseException { IRequest request = queue.findRequest(id); request.setRequestStatus(RequestStatus.APPROVED); diff --git a/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java b/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java index c89265783..a0731d5dc 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java +++ b/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java @@ -386,7 +386,25 @@ public class KeyRequestService extends PKIService implements KeyRequestResource } public Response generateSymKey(SymKeyGenerationRequest data) { - // TODO Auto-generated method stub - return null; + if (data == null) { + throw new BadRequestException("Invalid key generation request."); + } + + KeyRequestDAO dao = new KeyRequestDAO(); + KeyRequestInfo info; + try { + info = dao.submitRequest(data, uriInfo); + auditArchivalRequestMade(info.getRequestId(), ILogger.SUCCESS, data.getClientId()); + + return Response + .created(new URI(info.getRequestURL())) + .entity(info) + .type(MediaType.APPLICATION_XML) + .build(); + } catch (EBaseException | URISyntaxException e) { + e.printStackTrace(); + auditArchivalRequestMade(null, ILogger.FAILURE, data.getClientId()); + throw new PKIException(e.toString()); + } } } |