diff options
8 files changed, 1300 insertions, 327 deletions
diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg index 2d7cb9895..e8003603b 100644 --- a/base/ca/shared/conf/CS.cfg +++ b/base/ca/shared/conf/CS.cfg @@ -731,7 +731,7 @@ ca.publish.rule.instance.LdapXCertRule.predicate= ca.publish.rule.instance.LdapXCertRule.publisher=LdapCrossCertPairPublisher ca.publish.rule.instance.LdapXCertRule.type=xcert cmc.cert.confirmRequired=false -cmc.lraPopWitness.verify.allow=true +cmc.lraPopWitness.verify.allow=false cmc.revokeCert.verify=true cmc.revokeCert.sharedSecret.class=com.netscape.cms.authentication.SharedSecret cmc.sharedSecret.class=com.netscape.cms.authentication.SharedSecret diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java index bca569bab..a2aca8ad1 100644 --- a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java +++ b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java @@ -31,6 +31,8 @@ import java.io.PrintStream; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Date; import java.util.StringTokenizer; @@ -50,22 +52,33 @@ import org.mozilla.jss.asn1.UTF8String; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.DigestAlgorithm; import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.PrivateKey; import org.mozilla.jss.crypto.SignatureAlgorithm; +import org.mozilla.jss.crypto.SymmetricKey; import org.mozilla.jss.crypto.X509Certificate; import org.mozilla.jss.pkcs10.CertificationRequest; import org.mozilla.jss.pkix.cmc.CMCCertId; +import org.mozilla.jss.pkix.cmc.CMCStatusInfo; +import org.mozilla.jss.pkix.cmc.DecryptedPOP; +import org.mozilla.jss.pkix.cmc.EncryptedPOP; import org.mozilla.jss.pkix.cmc.GetCert; import org.mozilla.jss.pkix.cmc.IdentityProofV2; import org.mozilla.jss.pkix.cmc.LraPopWitness; +import org.mozilla.jss.pkix.cmc.OtherInfo; import org.mozilla.jss.pkix.cmc.OtherMsg; import org.mozilla.jss.pkix.cmc.PKIData; +import org.mozilla.jss.pkix.cmc.PendInfo; +import org.mozilla.jss.pkix.cmc.ResponseBody; import org.mozilla.jss.pkix.cmc.TaggedAttribute; import org.mozilla.jss.pkix.cmc.TaggedCertificationRequest; import org.mozilla.jss.pkix.cmc.TaggedRequest; import org.mozilla.jss.pkix.cmmf.RevRequest; import org.mozilla.jss.pkix.cms.ContentInfo; import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo; +import org.mozilla.jss.pkix.cms.EncryptedContentInfo; +import org.mozilla.jss.pkix.cms.EnvelopedData; import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber; +import org.mozilla.jss.pkix.cms.RecipientInfo; import org.mozilla.jss.pkix.cms.SignedData; import org.mozilla.jss.pkix.cms.SignerIdentifier; import org.mozilla.jss.pkix.cms.SignerInfo; @@ -129,33 +142,31 @@ public class CMCRequest { X509Certificate cert = getCertificate(tokenName, nickname); if (cert != null) - System.out.println("got signing cert"); + System.out.println("getPrivateKey: got signing cert"); return CryptoManager.getInstance().findPrivKeyByCert(cert); } /** - * getCMCBlob create and return the enrollment request. - * <P> + * signData signs the request PKIData * * @param signerCert the certificate of the authorized signer of the CMC revocation request. * @param nickname the nickname of the certificate inside the token. - * @param rValue CRMF/PKCS10 request. - * @param format either crmf or pkcs10 - * @return the CMC enrollment request encoded in base64 + * @param pkidata the request PKIData to be signed + * + * @return the SignedData + * */ - static ContentInfo getCMCBlob(X509Certificate signerCert, String tokenName, String nickname, - String[] rValue, String format, CryptoManager manager, String transactionMgtEnable, - String transactionMgtId, - String identificationEnable, String identification, - String identityProofEnable, String identityProofSharedSecret, - String identityProofV2Enable, String identityProofV2SharedSecret, - String identityProofV2hashAlg, String identityProofV2macAlg, - SEQUENCE controlSeq, SEQUENCE otherMsgSeq, int bpid) { - - System.out.println("in getCMCBlob"); + static SignedData signData( + X509Certificate signerCert, + String tokenName, + String nickname, + CryptoManager manager, + PKIData pkidata) { + String method = "signData: "; + SignedData req = null; + System.out.println(method + "begins: "); - ContentInfo fullEnrollmentReq = null; try { java.security.PrivateKey privKey = null; SignerIdentifier si = null; @@ -175,8 +186,122 @@ public class CMCRequest { SignerIdentifier.ISSUER_AND_SERIALNUMBER, ias, null); privKey = getPrivateKey(tokenName, nickname); if (privKey != null) - System.out.println("getCMCBlob: got privKey"); + System.out.println(method + " got signer privKey"); + + EncapsulatedContentInfo ci = new EncapsulatedContentInfo(OBJECT_IDENTIFIER.id_cct_PKIData, pkidata); + DigestAlgorithm digestAlg = null; + SignatureAlgorithm signAlg = null; + org.mozilla.jss.crypto.PrivateKey.Type signingKeyType = ((org.mozilla.jss.crypto.PrivateKey) privKey) + .getType(); + if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.RSA)) { + signAlg = SignatureAlgorithm.RSASignatureWithSHA256Digest; + } else if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.EC)) { + signAlg = SignatureAlgorithm.ECSignatureWithSHA256Digest; + } else { + System.out.println("Algorithm not supported"); + return null; + } + + MessageDigest SHADigest = null; + + byte[] digest = null; + try { + SHADigest = MessageDigest.getInstance("SHA256"); + digestAlg = DigestAlgorithm.SHA256; + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + + pkidata.encode(ostream); + digest = SHADigest.digest(ostream.toByteArray()); + } catch (NoSuchAlgorithmException e) { + System.out.println(e); System.exit(1);} + System.out.println(method + "digest created for pkidata"); + + SignerInfo signInfo = new SignerInfo(si, null, null, OBJECT_IDENTIFIER.id_cct_PKIData, digest, signAlg, + (org.mozilla.jss.crypto.PrivateKey) privKey); + SET signInfos = new SET(); + signInfos.addElement(signInfo); + + SET digestAlgs = new SET(); + + if (digestAlg != null) { + AlgorithmIdentifier ai = new AlgorithmIdentifier(digestAlg.toOID(), null); + digestAlgs.addElement(ai); + } + + org.mozilla.jss.crypto.X509Certificate[] agentChain = manager.buildCertificateChain(signerCert); + SET certs = new SET(); + + for (int i = 0; i < agentChain.length; i++) { + ANY cert = new ANY(agentChain[i].getEncoded()); + certs.addElement(cert); + } + + req = new SignedData(digestAlgs, ci, certs, null, signInfos); + System.out.println(method + "signed request generated."); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + return req; + } + + /** + * getCMCBlob create and return the enrollment request. + * + * @return the CMC enrollment request encoded in base64 + * + */ + static ContentInfo getCMCBlob(SignedData req) { + String method = "getCMCBlob: "; + System.out.println(method + "begins"); + ContentInfo fullEnrollmentReq = new ContentInfo(req); + try { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bs); + + if (fullEnrollmentReq != null) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + fullEnrollmentReq.encode(os); + ps.print(Utils.base64encode(os.toByteArray())); + } + String asciiBASE64Blob = bs.toString(); + + System.out.println(""); + System.out.println("The CMC enrollment request in base-64 encoded format:"); + System.out.println(""); + System.out.println(asciiBASE64Blob); + } catch (Exception e) { + System.out.println(method + " Exception:" + e.toString()); + System.exit(1); + } + return fullEnrollmentReq; + } + + /** + * createPKIData creates PKIData + * + * @param rValue CRMF/PKCS10 request. + * @param format either crmf or pkcs10 + * @return request in PKIData + */ + static PKIData createPKIData( + String[] rValue, String format, String transactionMgtEnable, + String transactionMgtId, + String identificationEnable, String identification, + String identityProofEnable, String identityProofSharedSecret, + String identityProofV2Enable, String witnessSharedSecret, + String identityProofV2hashAlg, String identityProofV2macAlg, + SEQUENCE controlSeq, SEQUENCE otherMsgSeq, int bpid) { + + String method = "createPKIData: "; + + System.out.println(method + "begins"); + PKIData pkidata = null; + + try { TaggedRequest trq = null; PKCS10 pkcs = null; CertReqMsg certReqMsg = null; @@ -185,20 +310,18 @@ public class CMCRequest { SEQUENCE reqSequence = new SEQUENCE(); try { for (int k = 0; k < rValue.length; k++) { - System.out.println("k="+ k); + System.out.println("k=" + k); String asciiBASE64Blob = rValue[k]; byte[] decodedBytes = Utils.base64decode(asciiBASE64Blob); if (format.equals("crmf")) { - System.out.println("getCMCBlob: format: crmf"); - ByteArrayInputStream reqBlob = - new ByteArrayInputStream(decodedBytes); + System.out.println(method + " format: crmf"); + ByteArrayInputStream reqBlob = new ByteArrayInputStream(decodedBytes); SEQUENCE crmfMsgs = null; try { - crmfMsgs = (SEQUENCE) new SEQUENCE.OF_Template(new - CertReqMsg.Template()).decode(reqBlob); + crmfMsgs = (SEQUENCE) new SEQUENCE.OF_Template(new CertReqMsg.Template()).decode(reqBlob); } catch (InvalidBERException ee) { - System.out.println("getCMCBlob: This is not a crmf request. Or this request has an error."); + System.out.println(method + " This is not a crmf request. Or this request has an error."); System.exit(1); } certReqMsg = (CertReqMsg) crmfMsgs.elementAt(0); @@ -208,25 +331,24 @@ public class CMCRequest { try { pkcs = new PKCS10(decodedBytes, true); } catch (Exception e2) { - System.out.println("getCMCBlob: Excception:"+e2.toString()); + System.out.println(method + " Excception:" + e2.toString()); System.exit(1); } ByteArrayInputStream crInputStream = new ByteArrayInputStream( pkcs.toByteArray()); - CertificationRequest cr = (CertificationRequest) - CertificationRequest.getTemplate().decode(crInputStream); + CertificationRequest cr = (CertificationRequest) CertificationRequest.getTemplate() + .decode(crInputStream); TaggedCertificationRequest tcr = new TaggedCertificationRequest( new INTEGER(bpid++), cr); - trq = new - TaggedRequest(TaggedRequest.PKCS10, tcr, null); + trq = new TaggedRequest(TaggedRequest.PKCS10, tcr, null); } else { - System.out.println("getCMCBlob: Unrecognized request format: " + format); + System.out.println(method + " Unrecognized request format: " + format); System.exit(1); } reqSequence.addElement(trq); } } catch (Exception e) { - System.out.println("getCMCBlob: Exception:"+ e.toString()); + System.out.println(method + " Exception:" + e.toString()); System.exit(1); } @@ -242,7 +364,7 @@ public class CMCRequest { // if both, V2 takes precedence if (identityProofV2Enable.equals("true")) { bpid = addIdentityProofV2Attr(bpid, controlSeq, reqSequence, - identityProofV2SharedSecret, + witnessSharedSecret, (identificationEnable.equals("true")) ? identification : null, identityProofV2hashAlg, identityProofV2macAlg); } else if (identityProofEnable.equals("true")) { @@ -250,77 +372,12 @@ public class CMCRequest { identityProofSharedSecret); } - PKIData pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq); - - EncapsulatedContentInfo ci = new - EncapsulatedContentInfo(OBJECT_IDENTIFIER.id_cct_PKIData, pkidata); - // SHA1 is the default digest Alg for now. - DigestAlgorithm digestAlg = null; - SignatureAlgorithm signAlg = null; - org.mozilla.jss.crypto.PrivateKey.Type signingKeyType = ((org.mozilla.jss.crypto.PrivateKey) privKey).getType(); - if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.RSA)) { - signAlg = SignatureAlgorithm.RSASignatureWithSHA1Digest; - } else if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.EC)) { - signAlg = SignatureAlgorithm.ECSignatureWithSHA1Digest; - } else if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.DSA)) { - signAlg = SignatureAlgorithm.DSASignatureWithSHA1Digest; - } - - MessageDigest SHADigest = null; - - byte[] digest = null; - try { - SHADigest = MessageDigest.getInstance("SHA1"); - digestAlg = DigestAlgorithm.SHA1; - - ByteArrayOutputStream ostream = new ByteArrayOutputStream(); - - pkidata.encode(ostream); - digest = SHADigest.digest(ostream.toByteArray()); - } catch (NoSuchAlgorithmException e) { - } - SignerInfo signInfo = new - SignerInfo(si, null, null, OBJECT_IDENTIFIER.id_cct_PKIData, digest, signAlg, - (org.mozilla.jss.crypto.PrivateKey) privKey); - SET signInfos = new SET(); - signInfos.addElement(signInfo); - - SET digestAlgs = new SET(); - - if (digestAlg != null) { - AlgorithmIdentifier ai = new AlgorithmIdentifier(digestAlg.toOID(), null); - digestAlgs.addElement(ai); - } - - org.mozilla.jss.crypto.X509Certificate[] agentChain = manager.buildCertificateChain(signerCert); - SET certs = new SET(); - - for (int i = 0; i < agentChain.length; i++) { - ANY cert = new ANY(agentChain[i].getEncoded()); - certs.addElement(cert); - } - SignedData req = new SignedData(digestAlgs, ci, certs, null, signInfos); - fullEnrollmentReq = new ContentInfo(req); - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(bs); - - if (fullEnrollmentReq != null) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - fullEnrollmentReq.encode(os); - ps.print(Utils.base64encode(os.toByteArray())); - } - String asciiBASE64Blob = bs.toString(); - - System.out.println(""); - System.out.println("The CMC enrollment request in base-64 encoded format:"); - System.out.println(""); - System.out.println(asciiBASE64Blob); + pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq); } catch (Exception e) { e.printStackTrace(); System.exit(1); } - return fullEnrollmentReq; + return pkidata; } static void printUsage() { @@ -330,16 +387,29 @@ public class CMCRequest { System.out.println(""); System.out.println("The configuration file should look like as follows:"); System.out.println(""); + System.out.println("#decryptedPop.enable: if true, this is in response to an encryptedPOP request from the server from previous request;"); + System.out.println("# most all options other than the following are ignored:"); + System.out.println("# encryptedPopResponseFile, privKeyId, decryptedPopRequestFile"); + System.out.println("# where"); + System.out.println("# encryptPopResponse was the output from previous request, and is now an input to the new request that is about to be generated"); + System.out.println("# decryptedPopRequestFile is the output that is to be sent to the server as 2nd trip request in response to encryptedPOP"); + System.out.println("# privKeyId: used for decrypting the encryptedPOP and creating decryptedPOP"); + System.out.println(""); + System.out.println("decryptedPop.enable=false"); + System.out.println("encryptedPopResponseFile=cmc.resp"); + System.out.println("request.privKeyId="); + System.out.println("decryptedPopRequestFile=cmc.decreyptedPOP.req"); + System.out.println(""); System.out.println("#numRequests: Total number of PKCS10 requests or CRMF requests."); System.out.println("numRequests=1"); System.out.println(""); System.out.println("#input: full path for the PKCS10 request or CRMF request,"); System.out.println("#the content must be in Base-64 encoded format"); System.out.println("#Multiple files are supported. They must be separated by space."); - System.out.println("input=crmf1"); + System.out.println("input=crmf.req"); System.out.println(""); System.out.println("#output: full path for the CMC request in binary format"); - System.out.println("output=/u/doc/cmcReq"); + System.out.println("output=cmc.req"); System.out.println(""); System.out.println("#tokenname: name of token where agent signing cert can be found (default is internal)"); System.out.println("tokenname=internal"); @@ -349,7 +419,7 @@ public class CMCRequest { System.out.println("nickname=CMS Agent Certificate"); System.out.println(""); System.out.println("#dbdir: directory for cert8.db, key3.db and secmod.db"); - System.out.println("dbdir=/u/smith/.netscape"); + System.out.println("dbdir=./"); System.out.println(""); System.out.println("#password: password for cert8.db which stores the agent"); System.out.println("#certificate"); @@ -360,7 +430,7 @@ public class CMCRequest { System.out.println(""); System.out.println("#confirmCertAcceptance.enable: if true, then the request will"); System.out.println("#contain this control. Otherwise, false."); - System.out.println("confirmCertAcceptance.enable=true"); + System.out.println("confirmCertAcceptance.enable=false"); System.out.println(""); System.out.println("#confirmCertAcceptance.serial: The serial number for"); System.out.println("#confirmCertAcceptance control"); @@ -372,7 +442,7 @@ public class CMCRequest { System.out.println(""); System.out.println("#getCert.enable: if true, then the request will contain this"); System.out.println("#control. Otherwise, false."); - System.out.println("getCert.enable=true"); + System.out.println("getCert.enable=false"); System.out.println(""); System.out.println("#getCert.serial: The serial number for getCert control"); System.out.println("getCert.serial=3"); @@ -382,14 +452,14 @@ public class CMCRequest { System.out.println(""); System.out.println("#dataReturn.enable: if true, then the request will contain"); System.out.println("#this control. Otherwise, false."); - System.out.println("dataReturn.enable=true"); + System.out.println("dataReturn.enable=false"); System.out.println(""); System.out.println("#dataReturn.data: data contained in the control."); System.out.println("dataReturn.data=test"); System.out.println(""); System.out.println("#transactionMgt.enable: if true, then the request will contain"); System.out.println("#this control. Otherwise, false."); - System.out.println("transactionMgt.enable=true"); + System.out.println("transactionMgt.enable=false"); System.out.println(""); System.out.println("#transactionMgt.id: transaction identifier. Verisign recommend"); System.out.println("#transactionId to be MD5 hash of publicKey."); @@ -397,14 +467,14 @@ public class CMCRequest { System.out.println(""); System.out.println("#senderNonce.enable: if true, then the request will contain this"); System.out.println("#control. Otherwise, false."); - System.out.println("senderNonce.enable=true"); + System.out.println("senderNonce.enable=false"); System.out.println(""); System.out.println("#senderNonce.id: sender nonce"); System.out.println("senderNonce.id="); System.out.println(""); System.out.println("#revRequest.enable: if true, then the request will contain this"); System.out.println("#control. Otherwise, false."); - System.out.println("revRequest.enable=true"); + System.out.println("revRequest.enable=false"); System.out.println(""); System.out.println("#revRequest.nickname: The nickname for the revoke certificate"); System.out.println("revRequest.nickname=newuser's 102504a ID"); @@ -439,15 +509,15 @@ public class CMCRequest { System.out.println("#Note that if both identityProof and identityProofV2"); System.out.println("# are enabled, identityProofV2 takes precedence; Only one of them can be active at a time"); System.out.println("#Supported hashAlg are:"); - System.out.println("# SHA-1, SHA-256, SHA-384, and SHA-512"); + System.out.println("# SHA-256, SHA-384, and SHA-512"); System.out.println("#Supported macAlg are:"); - System.out.println("# SHA-1-HMAC, SHA-256-HMAC, SHA-384-HMAC, and SHA-512-HMAC"); + System.out.println("# SHA-256-HMAC, SHA-384-HMAC, and SHA-512-HMAC"); System.out.println("identityProofV2.enable=false"); System.out.println("identityProofV2.hashAlg=SHA-256"); System.out.println("identityProofV2.macAlg=SHA-256-HMAC"); System.out.println(""); - System.out.println("#identityProofV2.sharedSecret: Shared Secret"); - System.out.println("identityProofV2.sharedSecret=testing"); + System.out.println("#witness.sharedSecret: Shared Secret"); + System.out.println("witness.sharedSecret=testing"); System.out.println(""); System.out.println("#identification works with identityProofV2"); System.out.println("identification.enable=false"); @@ -471,7 +541,7 @@ public class CMCRequest { System.out.println(""); System.out.println("#LraPopWitness.enable: if true, then the request will contain this"); System.out.println("#control. Otherwise, false."); - System.out.println("LraPopWitness.enable=true"); + System.out.println("LraPopWitness.enable=false"); System.out.println(""); System.out.println("#LraPopWitness.bodyPartIDs: List of body part IDs"); System.out.println("#Each id is separated by space."); @@ -550,7 +620,7 @@ public class CMCRequest { * @param sharedSecret shared secret * @param hashAlgString hash algorithm * @param macAlgString mac algorithm - * cfu + * @author cfu */ private static int addIdentityProofV2Attr(int bpid, SEQUENCE seq, SEQUENCE reqSequence, @@ -926,7 +996,7 @@ public class CMCRequest { * @param seq * @param ident * @return - * cfu + * @author cfu */ private static int addIdentificationAttr(int bpid, SEQUENCE seq, String ident) { UTF8String ident_s = null; @@ -973,11 +1043,273 @@ public class CMCRequest { return bpid; } + /** + * processEncryptedPopResponse parses previous CMC response + * and returns the encryptedPop + * + * @param prevResponse file + * @param privKey + * @return encryptedPop and reqIdString in Object[] + * @author cfu + */ + private static Object[] processEncryptedPopResponse( + String prevResponse) { + // the values to be returned + EncryptedPOP encryptedPop = null; + String reqIdString = null; // capture the requestId; + + String method = "processEncryptedPopResponse: "; + System.out.println(method + " begins."); + + byte[] bb = new byte[10000]; + FileInputStream fis = null; + try { + fis = new FileInputStream(prevResponse); + while (fis.available() > 0) + fis.read(bb, 0, 10000); + } catch (Exception e) { + System.out.println(method + "Error reading the response. Exception: " + e.toString()); + System.exit(1); + } + System.out.println(method + " previous response read."); + + org.mozilla.jss.pkix.cms.SignedData cmcFullResp = null; + try { + ByteArrayInputStream bis = new ByteArrayInputStream(bb); + org.mozilla.jss.pkix.cms.ContentInfo cii = (org.mozilla.jss.pkix.cms.ContentInfo) org.mozilla.jss.pkix.cms.ContentInfo + .getTemplate().decode(bis); + + cmcFullResp = (org.mozilla.jss.pkix.cms.SignedData) cii.getInterpretedContent(); + EncapsulatedContentInfo ci = cmcFullResp.getContentInfo(); + OBJECT_IDENTIFIER id = ci.getContentType(); + OBJECT_IDENTIFIER dataid = new OBJECT_IDENTIFIER("1.2.840.113549.1.7.1"); + if (!id.equals(OBJECT_IDENTIFIER.id_cct_PKIResponse) && !id.equals(dataid)) { + System.out.println(method + "Invalid CMC Response Format"); + } + + if (!ci.hasContent()) + return null; + + OCTET_STRING content1 = ci.getContent(); + ByteArrayInputStream bbis = new ByteArrayInputStream(content1.toByteArray()); + ResponseBody responseBody = (ResponseBody) (new ResponseBody.Template()).decode(bbis); + SEQUENCE controlSequence = responseBody.getControlSequence(); + int numControls = controlSequence.size(); + System.out.println(method + "Number of controls is " + numControls); + + for (int i = 0; i < numControls; i++) { + TaggedAttribute taggedAttr = (TaggedAttribute) controlSequence.elementAt(i); + OBJECT_IDENTIFIER type = taggedAttr.getType(); + + if (type.equals(OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo)) { + System.out.println(method + "Control #" + i + ": CMCStatusInfo"); + System.out.println(method + " OID: " + type.toString()); + SET sts = taggedAttr.getValues(); + int numSts = sts.size(); + for (int j = 0; j < numSts; j++) { + CMCStatusInfo cst = (CMCStatusInfo) ASN1Util.decode(CMCStatusInfo.getTemplate(), + ASN1Util.encode(sts.elementAt(j))); + SEQUENCE seq = cst.getBodyList(); + StringBuilder s = new StringBuilder(" BodyList: "); + for (int k = 0; k < seq.size(); k++) { + INTEGER n = (INTEGER) seq.elementAt(k); + s.append(n.toString() + " "); + } + System.out.println(method + s); + int st = cst.getStatus(); + if (st != CMCStatusInfo.SUCCESS && st != CMCStatusInfo.CONFIRM_REQUIRED) { + String stString = cst.getStatusString(); + if (stString != null) + System.out.println(method + " Status String: " + stString); + OtherInfo oi = cst.getOtherInfo(); + OtherInfo.Type t = oi.getType(); + if (t == OtherInfo.FAIL) { + System.out.println(method + " OtherInfo type: FAIL"); + System.out.println(method + + " not what we expected, because encryptedPOP.enable is true!!!! exit now"); + System.exit(1); + } else if (t == OtherInfo.PEND) { + System.out.println(method + " OtherInfo type: PEND"); + PendInfo pi = oi.getPendInfo(); + if (pi == null) { + System.out.println(method + "PendInfo null...skipping"); + continue; + } else + System.out.println(method + "PendInfo present...processing..."); + if (pi.getPendTime() != null) { + String datePattern = "dd/MMM/yyyy:HH:mm:ss z"; + SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern); + Date d = pi.getPendTime().toDate(); + System.out.println(method + " Date: " + dateFormat.format(d)); + } + OCTET_STRING pendToken = pi.getPendToken(); + if (pendToken != null) { + byte reqId[] = pendToken.toByteArray(); + reqIdString = new String(reqId); + System.out.println(method + " Pending request id: " + reqIdString); + } else { + System.out.println(method + "missing pendToken in response"); + System.exit(1); + } + System.out.println(method + " what we expected, as encryptedPOP.enable is true;"); + } + } else if (st == CMCStatusInfo.SUCCESS) { + System.out.println(method + " Status: SUCCESS"); + System.out.println( + method + " not what we expected, because encryptedPOP.enable is true!!!! exit now"); + System.exit(1); + } + } + } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_encryptedPOP)) { + // bingo + System.out.println(method + "Control #" + i + ": CMC encrypted POP"); + System.out.println(method + " OID: " + type.toString()); + SET encryptedPOPvals = taggedAttr.getValues(); + + encryptedPop = (EncryptedPOP) (ASN1Util.decode(EncryptedPOP.getTemplate(), + ASN1Util.encode(encryptedPOPvals.elementAt(0)))); + System.out.println(method + "encryptedPOP decoded successfully"); + + } // we don't expect any other controls + } //for + } catch (Exception e) { + System.out.println(method + e); + System.exit(1); + } + + System.out.println(method + "ends"); + return new Object[] { encryptedPop, reqIdString }; + } + + /** + * constructDecryptedPopRequest constructs request PKIData for DecryptedPOP + * + * @param encryptedPopInfo {EncryptedPOP, reqIdString} + * @param privKey + * @return request PKIData + * @author cfu + */ + private static PKIData constructDecryptedPopRequest( + Object[] encryptedPopInfo, + String tokenName, + PrivateKey privKey) { + PKIData pkidata = null; + DecryptedPOP decryptedPop = null; + + String method = "constructDecryptedPopRequest: "; + System.out.println(method + "begins"); + if ((encryptedPopInfo == null) || (privKey == null)) { + System.out.println(method + "input params encryptedPopInfo and privKey cannot be null"); + System.exit(1); + } + + EncryptedPOP encryptedPop = (EncryptedPOP) encryptedPopInfo[0]; + String reqIdString = (String) encryptedPopInfo[1]; + if ((encryptedPop == null) || (reqIdString == null)) { + System.out.println(method + "encryptedPopInfo content encryptedPop and reqIdString cannot be null"); + System.exit(1); + } + + try { + TaggedRequest request = encryptedPop.getRequest(); + AlgorithmIdentifier thePOPAlgID = encryptedPop.getThePOPAlgID(); + AlgorithmIdentifier witnessAlgID = encryptedPop.getWitnessAlgID(); + OCTET_STRING witness = encryptedPop.getWitness(); + ContentInfo cms = encryptedPop.getContentInfo(); + EnvelopedData envData = (EnvelopedData) cms.getInterpretedContent(); + EncryptedContentInfo encCI = envData.getEncryptedContentInfo(); + SET recipients = envData.getRecipientInfos(); + RecipientInfo recipient = (RecipientInfo) (ASN1Util.decode(RecipientInfo.getTemplate(), + ASN1Util.encode(recipients.elementAt(0)))); + System.out.println(method + " previous response parsed."); + + CryptoToken token = CryptoUtil.getKeyStorageToken(tokenName); + SymmetricKey symKey = CryptoUtil.unwrap( + token, + SymmetricKey.Usage.DECRYPT, + privKey, + recipient.getEncryptedKey().toByteArray()); + if (symKey == null) { + System.out.println(method + "symKey returned null from CryptoUtil.unwrap(). Abort!"); + System.exit(1); + } + System.out.println(method + "symKey unwrapped."); + + byte challenge[] = CryptoUtil.decryptUsingSymmetricKey( + token, + encCI.getEncryptedContent().toByteArray(), + symKey); + if (challenge == null) { + System.out + .println(method + "challenge returned null from CryptoUtil.decryptUsingSymmetricKey(). Abort!"); + System.exit(1); + } + System.out.println(method + "challenge decrypted."); + + // now verify the witness + try { + MessageDigest hash = MessageDigest.getInstance(CryptoUtil.getNameFromHashAlgorithm(witnessAlgID)); + byte[] digest = hash.digest(challenge); + boolean witnessChecked = Arrays.equals(digest, witness.toByteArray()); + if (witnessChecked) { + System.out.println(method + "Yay! witness verified"); + } else { + System.out.println(method + "Oops! witness failed to verify. Must abort!"); + System.exit(1); + } + } catch (Exception ex) { + System.out.println(method + ex); + System.exit(1); + } + + // now calculate the POP Proof Value + byte[] popProofValue = null; + try { + System.out.println(method + "calculating POP Proof Value"); + MessageDigest SHA2Digest = MessageDigest.getInstance("SHA256"); + HMACDigest hmacDigest = new HMACDigest(SHA2Digest, challenge); + hmacDigest.update(ASN1Util.encode(request)); + popProofValue = hmacDigest.digest(); + } catch (Exception ex) { + System.out.println(method + "calculating POP Proof Value failed: " + ex); + System.exit(1); + } + + int bpid = 1; + // now construct DecryptedPOP + System.out.println(method + "constructing DecryptedPOP..."); + decryptedPop = new DecryptedPOP(new INTEGER(reqIdString), thePOPAlgID, new OCTET_STRING(popProofValue)); + System.out.println(method + "DecryptedPOP constructed successfully"); + System.out.println(method + "adding decryptedPop control"); + TaggedAttribute decPop = new TaggedAttribute(new INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_decryptedPOP, + decryptedPop); + + SEQUENCE reqSequence = new SEQUENCE(); + reqSequence.addElement(request); //stuff original req + + SEQUENCE controlSeq = new SEQUENCE(); + controlSeq.addElement(decPop); + System.out.println(method + "decryptedPop control added"); + + SEQUENCE otherMsgSeq = new SEQUENCE(); + + pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq); + } catch (Exception e) { + System.out.println(method + e); + System.exit(1); + } + + System.out.println(method + " completes."); + return pkidata; + } + public static void main(String[] s) { String numRequests = null; String dbdir = null, nickname = null; String tokenName = null; String ifilename = null, ofilename = null, password = null, format = null; + String decryptedPopEnable = "false", encryptedPopResponseFile=null, privKeyId = null, decryptedPopRequestFile= null; String confirmCertEnable = "false", confirmCertIssuer = null, confirmCertSerial = null; String getCertEnable = "false", getCertIssuer = null, getCertSerial = null; String dataReturnEnable = "false", dataReturnData = null; @@ -989,7 +1321,7 @@ public class CMCRequest { String revRequestInvalidityDatePresent = "false"; String identificationEnable = "false", identification = null; String identityProofEnable = "false", identityProofSharedSecret = null; - String identityProofV2Enable = "false", identityProofV2SharedSecret = null, identityProofV2hashAlg = "SHA256", identityProofV2macAlg = "SHA256"; + String identityProofV2Enable = "false", witnessSharedSecret = null, identityProofV2hashAlg = "SHA256", identityProofV2macAlg = "SHA256"; String popLinkWitnessEnable = "false"; String bodyPartIDs = null, lraPopWitnessEnable = "false"; @@ -1046,6 +1378,14 @@ public class CMCRequest { ofilename = val; } else if (name.equals("input")) { ifilename = val; + } else if (name.equals("decryptedPop.enable")) { + decryptedPopEnable = val; + } else if (name.equals("encryptedPopResponseFile")) { + encryptedPopResponseFile = val; + } else if (name.equals("request.privKeyId")) { + privKeyId = val; + } else if (name.equals("decryptedPopRequestFile")) { + decryptedPopRequestFile = val; } else if (name.equals("confirmCertAcceptance.serial")) { confirmCertSerial = val; } else if (name.equals("confirmCertAcceptance.issuer")) { @@ -1092,8 +1432,8 @@ public class CMCRequest { identification = val; } else if (name.equals("identityProofV2.enable")) { identityProofV2Enable = val; - } else if (name.equals("identityProofV2.sharedSecret")) { - identityProofV2SharedSecret = val; + } else if (name.equals("witness.sharedSecret")) { + witnessSharedSecret = val; } else if (name.equals("identityProofV2.hashAlg")) { identityProofV2hashAlg = val; } else if (name.equals("identityProofV2.macAlg")) { @@ -1118,45 +1458,6 @@ public class CMCRequest { printUsage(); } - if (ifilename == null) { - System.out.println("Missing input filename for PKCS10 or CRMF."); - printUsage(); - } - - int num = 0; - if (numRequests == null) { - System.out.println("Missing numRequests."); - printUsage(); - } else { - try { - num = Integer.parseInt(numRequests); - } catch (Exception ee) { - System.out.println("numRequests must be integer"); - System.exit(1); - } - } - - StringTokenizer tokenizer = new StringTokenizer(ifilename, " "); - String[] ifiles = new String[num]; - for (int i = 0; i < num; i++) { - String ss = tokenizer.nextToken(); - ifiles[i] = ss; - if (ss == null) { - System.out.println("Missing input file for the request."); - System.exit(1); - } - } - - if (ofilename == null) { - System.out.println("Missing output filename for the CMC request."); - printUsage(); - } - - if (format == null) { - System.out.println("Missing format."); - printUsage(); - } - if (password == null) { System.out.println("Missing password."); printUsage(); @@ -1214,116 +1515,223 @@ public class CMCRequest { System.out.println("got signerCert: "+ certname.toString()); } - String[] requests = new String[num]; - for (int i = 0; i < num; i++) { - BufferedReader inputBlob = null; - try { - inputBlob = new BufferedReader(new InputStreamReader( - new BufferedInputStream(new FileInputStream(ifiles[i])))); - } catch (FileNotFoundException e) { - System.out.println("CMCRequest: can't find file " + - ifiles[i] + ":\n" + e); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - // (3) Read the entire contents of the specified BASE 64 encoded - // blob into a String() object throwing away any - // headers beginning with HEADER and any trailers beginning - // with TRAILER - String asciiBASE64BlobChunk = ""; - StringBuffer asciiBASE64Blob = new StringBuffer(); - - try { - while ((asciiBASE64BlobChunk = inputBlob.readLine()) != null) { - if (!(asciiBASE64BlobChunk.startsWith(HEADER)) && - !(asciiBASE64BlobChunk.startsWith(TRAILER))) { - asciiBASE64Blob.append(asciiBASE64BlobChunk.trim()); - } + //cfu + ContentInfo cmcblob = null; + PKIData pkidata = null; + if (decryptedPopEnable.equalsIgnoreCase("true")) { + PrivateKey privk = null; + if (privKeyId == null) { + System.out.println("ecryptedPop.enable = true, but privKeyId not specified."); + printUsage(); + } else { + System.out.println("got privKeyId: " + privKeyId); + + byte[] keyIDb = CryptoUtil.string2byte(privKeyId); + + privk = CryptoUtil.findPrivateKeyFromID(keyIDb); + + if (privk != null) { + System.out.println("got private key"); + // now we can use this to decrypt encryptedPOP + } else { + System.out.println("error getting private key null"); + System.exit(1); } - requests[i] = asciiBASE64Blob.toString(); - } catch (IOException e) { - System.out.println("CMCRequest: Unexpected BASE64 " + - "encoded error encountered in readLine():\n" + - e); } - // (4) Close the DataInputStream() object - try { - inputBlob.close(); - } catch (IOException e) { - System.out.println("CMCRequest(): Unexpected BASE64 " + - "encoded error encountered in close():\n" + e); + + if (encryptedPopResponseFile == null) { + System.out.println("ecryptedPop.enable = true, but encryptedPopResponseFile is not specified."); + printUsage(); } - } - SEQUENCE controlSeq = new SEQUENCE(); - int bpid = 1; - if (confirmCertEnable.equalsIgnoreCase("true")) { - if (confirmCertIssuer.length() == 0 || confirmCertSerial.length() == 0) { - System.out.println("Illegal parameters for confirm certificate acceptance control"); + if (decryptedPopRequestFile == null) { + System.out.println("ecryptedPop.enable = true, but decryptedPopRequestFile is not specified."); printUsage(); + } + ofilename = decryptedPopRequestFile; + + // now start processing decryptedPOP + Object[] encryptedPopInfo = processEncryptedPopResponse(encryptedPopResponseFile); + if (encryptedPopInfo == null) { + System.out.println("processEncryptedPopResponse() returns null"); System.exit(1); } - bpid = addConfirmCertAttr(bpid, controlSeq, confirmCertIssuer, confirmCertSerial); - } + pkidata = constructDecryptedPopRequest(encryptedPopInfo, tokenName, privk); - if (lraPopWitnessEnable.equalsIgnoreCase("true")) { - if (bodyPartIDs.length() == 0) { - System.out.println("Illegal parameters for Lra Pop Witness control"); - printUsage(); + if (pkidata == null) { + System.out.println("after constructDecryptedPopRequest, pkidata null. no good"); System.exit(1); } + } else { // !decryptedPopEnable - bpid = addLraPopWitnessAttr(bpid, controlSeq, bodyPartIDs); - } + if (ifilename == null) { + System.out.println("Missing input filename for PKCS10 or CRMF."); + printUsage(); + } - if (getCertEnable.equalsIgnoreCase("true")) { - if (getCertIssuer.length() == 0 || getCertSerial.length() == 0) { - System.out.println("Illegal parameters for get certificate control"); + int num = 0; + if (numRequests == null) { + System.out.println("Missing numRequests."); printUsage(); - System.exit(1); + } else { + try { + num = Integer.parseInt(numRequests); + } catch (Exception ee) { + System.out.println("numRequests must be integer"); + System.exit(1); + } } - bpid = addGetCertAttr(bpid, controlSeq, getCertIssuer, getCertSerial); - } + StringTokenizer tokenizer = new StringTokenizer(ifilename, " "); + String[] ifiles = new String[num]; + for (int i = 0; i < num; i++) { + String ss = tokenizer.nextToken(); + ifiles[i] = ss; + if (ss == null) { + System.out.println("Missing input file for the request."); + System.exit(1); + } + } - if (dataReturnEnable.equalsIgnoreCase("true")) { - if (dataReturnData.length() == 0) { - System.out.println("Illegal parameters for data return control"); + if (ofilename == null) { + System.out.println("Missing output filename for the CMC request."); printUsage(); - System.exit(1); } - bpid = addDataReturnAttr(bpid, controlSeq, dataReturnData); - } + if (format == null) { + System.out.println("Missing format."); + printUsage(); + } + String[] requests = new String[num]; + for (int i = 0; i < num; i++) { + BufferedReader inputBlob = null; + try { + inputBlob = new BufferedReader(new InputStreamReader( + new BufferedInputStream(new FileInputStream(ifiles[i])))); + } catch (FileNotFoundException e) { + System.out.println("CMCRequest: can't find file " + + ifiles[i] + ":\n" + e); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + // (3) Read the entire contents of the specified BASE 64 encoded + // blob into a String() object throwing away any + // headers beginning with HEADER and any trailers beginning + // with TRAILER + String asciiBASE64BlobChunk = ""; + StringBuffer asciiBASE64Blob = new StringBuffer(); + + try { + while ((asciiBASE64BlobChunk = inputBlob.readLine()) != null) { + if (!(asciiBASE64BlobChunk.startsWith(HEADER)) && + !(asciiBASE64BlobChunk.startsWith(TRAILER))) { + asciiBASE64Blob.append(asciiBASE64BlobChunk.trim()); + } + } + requests[i] = asciiBASE64Blob.toString(); + } catch (IOException e) { + System.out.println("CMCRequest: Unexpected BASE64 " + + "encoded error encountered in readLine():\n" + + e); + } + // (4) Close the DataInputStream() object + try { + inputBlob.close(); + } catch (IOException e) { + System.out.println("CMCRequest(): Unexpected BASE64 " + + "encoded error encountered in close():\n" + e); + } + } - if (senderNonceEnable.equalsIgnoreCase("true")) - bpid = addSenderNonceAttr(bpid, controlSeq, senderNonce); + SEQUENCE controlSeq = new SEQUENCE(); + int bpid = 1; + if (confirmCertEnable.equalsIgnoreCase("true")) { + if (confirmCertIssuer.length() == 0 || confirmCertSerial.length() == 0) { + System.out.println("Illegal parameters for confirm certificate acceptance control"); + printUsage(); + System.exit(1); + } + bpid = addConfirmCertAttr(bpid, controlSeq, confirmCertIssuer, confirmCertSerial); + } - if (popLinkWitnessEnable.equalsIgnoreCase("true")) - bpid = addPopLinkWitnessAttr(bpid, controlSeq); + if (lraPopWitnessEnable.equalsIgnoreCase("true")) { + if (bodyPartIDs.length() == 0) { + System.out.println("Illegal parameters for Lra Pop Witness control"); + printUsage(); + System.exit(1); + } - SEQUENCE otherMsgSeq = new SEQUENCE(); - if (revRequestEnable.equalsIgnoreCase("true")) { - if (revRequestIssuer.length() == 0 || revRequestSerial.length() == 0 || - revRequestReason.length() == 0) { - System.out.println("Illegal parameters for revRequest control"); - printUsage(); - System.exit(1); + bpid = addLraPopWitnessAttr(bpid, controlSeq, bodyPartIDs); + } + + if (getCertEnable.equalsIgnoreCase("true")) { + if (getCertIssuer.length() == 0 || getCertSerial.length() == 0) { + System.out.println("Illegal parameters for get certificate control"); + printUsage(); + System.exit(1); + } + + bpid = addGetCertAttr(bpid, controlSeq, getCertIssuer, getCertSerial); } - bpid = addRevRequestAttr(bpid, controlSeq, otherMsgSeq, token, tokenName, revCertNickname, - revRequestIssuer, revRequestSerial, revRequestReason, revRequestSharedSecret, - revRequestComment, revRequestInvalidityDatePresent, cm); + if (dataReturnEnable.equalsIgnoreCase("true")) { + if (dataReturnData.length() == 0) { + System.out.println("Illegal parameters for data return control"); + printUsage(); + System.exit(1); + } + + bpid = addDataReturnAttr(bpid, controlSeq, dataReturnData); + } + + if (senderNonceEnable.equalsIgnoreCase("true")) + bpid = addSenderNonceAttr(bpid, controlSeq, senderNonce); + + if (popLinkWitnessEnable.equalsIgnoreCase("true")) + bpid = addPopLinkWitnessAttr(bpid, controlSeq); + + SEQUENCE otherMsgSeq = new SEQUENCE(); + if (revRequestEnable.equalsIgnoreCase("true")) { + if (revRequestIssuer.length() == 0 || revRequestSerial.length() == 0 || + revRequestReason.length() == 0) { + System.out.println("Illegal parameters for revRequest control"); + printUsage(); + System.exit(1); + } + + bpid = addRevRequestAttr(bpid, controlSeq, otherMsgSeq, token, tokenName, revCertNickname, + revRequestIssuer, revRequestSerial, revRequestReason, revRequestSharedSecret, + revRequestComment, revRequestInvalidityDatePresent, cm); + } + + // create the request PKIData + pkidata = createPKIData( + requests, + format, transactionMgtEnable, transactionMgtId, + identificationEnable, identification, + identityProofEnable, identityProofSharedSecret, + identityProofV2Enable, witnessSharedSecret, + identityProofV2hashAlg, identityProofV2macAlg, + controlSeq, otherMsgSeq, bpid); + + if (pkidata == null) { + System.out.println("pkidata null after createPKIData(). Exiting with error"); + System.exit(1); + } } - ContentInfo cmcblob = getCMCBlob(signerCert, tokenName, nickname, requests, format, - cm, transactionMgtEnable, transactionMgtId, - identificationEnable, identification, - identityProofEnable, identityProofSharedSecret, - identityProofV2Enable, identityProofV2SharedSecret, - identityProofV2hashAlg, identityProofV2macAlg, - controlSeq, otherMsgSeq, bpid); + // sign the request + SignedData signedData = signData(signerCert, tokenName, nickname, cm, pkidata); + if (signedData == null) { + System.out.println("signData() returns null. Exiting with error"); + System.exit(1); + } + cmcblob = getCMCBlob(signedData); + if (cmcblob == null) { + System.out.println("getCMCBlob() returns null. Exiting with error"); + System.exit(1); + } // (6) Finally, print the actual CMC blob to the // specified output file diff --git a/base/java-tools/src/com/netscape/cmstools/CMCResponse.java b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java index 7bbba8797..4c7493443 100644 --- a/base/java-tools/src/com/netscape/cmstools/CMCResponse.java +++ b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java @@ -34,6 +34,7 @@ import org.mozilla.jss.asn1.SEQUENCE; import org.mozilla.jss.asn1.SET; import org.mozilla.jss.pkix.cert.Certificate; import org.mozilla.jss.pkix.cmc.CMCStatusInfo; +import org.mozilla.jss.pkix.cmc.EncryptedPOP; import org.mozilla.jss.pkix.cmc.OtherInfo; import org.mozilla.jss.pkix.cmc.PendInfo; import org.mozilla.jss.pkix.cmc.ResponseBody; @@ -140,12 +141,27 @@ public class CMCResponse { else if (t == OtherInfo.PEND) { System.out.println(" OtherInfo type: PEND"); PendInfo pi = oi.getPendInfo(); + if (pi == null) { + System.out.println("PendInfo null...skipping"); + continue; + } else + System.out.println("PendInfo present...processing..."); if (pi.getPendTime() != null) { String datePattern = "dd/MMM/yyyy:HH:mm:ss z"; SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern); Date d = pi.getPendTime().toDate(); System.out.println(" Date: " + dateFormat.format(d)); } + OCTET_STRING pendToken = pi.getPendToken(); + if (pendToken != null) { + byte reqId[] = pendToken.toByteArray(); + String reqIdString = new String(reqId); + System.out.println(" Pending request id: " + reqIdString); + } else { + System.out.println("pendToken not in response"); + System.exit(1); + } + } } else if (st == CMCStatusInfo.SUCCESS) { System.out.println(" Status: SUCCESS"); @@ -200,6 +216,16 @@ public class CMCResponse { s.append(" "); } System.out.println(s); + } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_encryptedPOP)) { + System.out.println("Control #" + i + ": CMC encrypted POP"); + System.out.println(" OID: " + type.toString()); + SET encryptedPOPvals = taggedAttr.getValues(); + + EncryptedPOP encryptedPOP = + (EncryptedPOP) (ASN1Util.decode(EncryptedPOP.getTemplate(), + ASN1Util.encode(encryptedPOPvals.elementAt(0)))); + System.out.println("after encryptedPOP encode"); + } } } catch (Exception e) { diff --git a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java index 5e53bee67..fafe956e7 100644 --- a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java +++ b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java @@ -433,6 +433,13 @@ public class CRMFPopClient { throw new Exception("Unknown algorithm: " + algorithm); } + // print out keyid to be used in cmc decryptPOP + PrivateKey privateKey = (PrivateKey) keyPair.getPrivate(); + @SuppressWarnings("deprecation") + byte id[] = privateKey.getUniqueID(); + String kid = CryptoUtil.byte2string(id); + System.out.println("Keypair private key id: " + kid); + if (verbose) System.out.println("Creating certificate request"); CertRequest certRequest = client.createCertRequest(token, transportCert, algorithm, keyPair, subject); diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java index 972d89f6a..5c07ecbc4 100644 --- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java +++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java @@ -24,10 +24,13 @@ import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; import java.security.cert.CertificateException; +import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.Locale; +import java.util.Random; import java.util.StringTokenizer; import org.mozilla.jss.CryptoManager; @@ -43,8 +46,11 @@ import org.mozilla.jss.asn1.UTF8String; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.DigestAlgorithm; import org.mozilla.jss.crypto.HMACAlgorithm; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.crypto.SymmetricKey; import org.mozilla.jss.pkcs10.CertificationRequest; import org.mozilla.jss.pkcs10.CertificationRequestInfo; +import org.mozilla.jss.pkix.cmc.DecryptedPOP; import org.mozilla.jss.pkix.cmc.IdentityProofV2; import org.mozilla.jss.pkix.cmc.LraPopWitness; import org.mozilla.jss.pkix.cmc.OtherMsg; @@ -78,6 +84,7 @@ import com.netscape.certsrv.profile.IEnrollProfile; import com.netscape.certsrv.profile.IProfileContext; import com.netscape.certsrv.request.IRequest; import com.netscape.certsrv.request.IRequestQueue; +import com.netscape.certsrv.request.RequestId; import com.netscape.cmsutil.crypto.CryptoUtil; import com.netscape.cmsutil.util.HMACDigest; @@ -162,13 +169,19 @@ public abstract class EnrollProfile extends BasicProfile num_requests = msgs.length; } + TaggedRequest[] cmc_msgs = null; if (cert_request_type != null && cert_request_type.startsWith("cmc")) { + /* + * TODO: cfu: Phase 2: check if CMCAuth pre-signed request passed. + * if not, identityProofV2 and/or identification controls + * are required; + */ // catch for invalid request - TaggedRequest[] msgs = parseCMC(locale, cert_request); - if (msgs == null) + cmc_msgs = parseCMC(locale, cert_request); + if (cmc_msgs == null) return null; else - num_requests = msgs.length; + num_requests = cmc_msgs.length; } // only 1 request for renewal @@ -191,6 +204,12 @@ public abstract class EnrollProfile extends BasicProfile result[i].setExtData(REQUEST_SEQ_NUM, renewal_seq_num); } else { result[i].setExtData(REQUEST_SEQ_NUM, Integer.valueOf(i)); + if ((cmc_msgs != null) && (cmc_msgs[i] != null)) { + CMS.debug("EnrollProfile: createRequests: setting cmc TaggedRequest in request"); + result[i].setExtData( + CTX_CERT_REQUEST, + ASN1Util.encode(cmc_msgs[i])); + } } if (locale != null) { result[i].setExtData(REQUEST_LOCALE, locale.getLanguage()); @@ -325,6 +344,93 @@ public abstract class EnrollProfile extends BasicProfile } /** + * setPOPchallenge generates a POP challenge and sets necessary info in request + * for composing encryptedPOP later + * + * @param IRequest the request + * @author cfu + */ + public void setPOPchallenge(IRequest req) throws EBaseException { + String method = "EnrollProfile: setPOPchallenge: "; + + CMS.debug(method + " getting user public key in request"); + byte[] req_key_data = req.getExtDataInByteArray(IEnrollProfile.REQUEST_KEY); + netscape.security.x509.CertificateX509Key pubKey = null; + if (req_key_data != null) { + CMS.debug(method + "found user public key in request"); + + // generate a challenge of 64 bytes; + Random random = new Random(); + byte[] challenge = new byte[64]; + random.nextBytes(challenge); + + ICertificateAuthority authority = (ICertificateAuthority) getAuthority(); + PublicKey issuanceProtPubKey = authority.getIssuanceProtPubKey(); + if (issuanceProtPubKey != null) + CMS.debug(method + "issuanceProtPubKey not null"); + else + CMS.debug(method + "issuanceProtPubKey null"); + + String msg = ""; + try { + CryptoToken token = null; + String tokenName = CMS.getConfigStore().getString("cmc.token", CryptoUtil.INTERNAL_TOKEN_NAME); + token = CryptoUtil.getCryptoToken(tokenName); + + PublicKey userPubKey = X509Key.parsePublicKey(new DerValue(req_key_data)); + + SymmetricKey symKey = CryptoUtil.generateKey(token); + byte[] pop_encreyptedData = CryptoUtil.encryptUsingSymmetricKey( + token, symKey, challenge); + if (pop_encreyptedData == null) { + msg = method + "pop_encreyptedData null"; + CMS.debug(msg); + throw new Exception(msg); + } + + byte[] pop_sysPubEncreyptedSession = CryptoUtil.wrapUsingPublicKey( + token, issuanceProtPubKey, symKey); + if (pop_sysPubEncreyptedSession == null) { + msg = method + "pop_sysPubEncreyptedSession null"; + CMS.debug(msg); + throw new Exception(msg); + } + + byte[] pop_userPubEncreyptedSession = CryptoUtil.wrapUsingPublicKey( + token, userPubKey, symKey); + if (pop_userPubEncreyptedSession == null) { + msg = method + "pop_userPubEncreyptedSession null"; + CMS.debug(msg); + throw new Exception(msg); + } + CMS.debug(method + "POP challenge fields generated successfully...setting request extData"); + + req.setExtData("pop_encreyptedData", pop_encreyptedData); + + req.setExtData("pop_sysPubEncreyptedSession", pop_sysPubEncreyptedSession); + + req.setExtData("pop_userPubEncreyptedSession", pop_userPubEncreyptedSession); + + // now compute and set witness + CMS.debug(method + "now compute and set witness"); + String hashName = CryptoUtil.getDefaultHashAlgName(); + CMS.debug(method + "hashName is " + hashName); + MessageDigest hash = MessageDigest.getInstance(hashName); + byte[] witness = hash.digest(challenge); + req.setExtData("pop_witness", witness); + + } catch (Exception e) { + CMS.debug(method + e); + throw new EBaseException(e.toString()); + } + + } else { + CMS.debug(method + " public key not found in request"); + throw new EBaseException(method + " public key not found in request"); + } + } + + /** * This method is called after the user submits the * request from the end-entity page. */ @@ -341,8 +447,14 @@ public abstract class EnrollProfile extends BasicProfile // process request // } // } + String method = "EnrollProfile: submit: "; IRequestQueue queue = getRequestQueue(); + String msg = ""; + + boolean popChallengeRequired = + request.getExtDataInBoolean("cmc_POPchallengeRequired", false); + CMS.debug(method + "popChallengeRequired =" + popChallengeRequired); // this profile queues request that is authenticated // by NoAuth @@ -350,26 +462,42 @@ public abstract class EnrollProfile extends BasicProfile queue.updateRequest(request); } catch (EBaseException e) { // save request to disk - CMS.debug("EnrollProfile: Unable to update request: " + e); + CMS.debug(method + " Unable to update request: " + e); CMS.debug(e); } - if (token == null) { - CMS.debug("EnrollProfile: auth token is null"); - CMS.debug("EnrollProfile: validating request"); + if (token == null){ + CMS.debug(method + " auth token is null"); + CMS.debug(method + " validating request"); validate(request); try { queue.updateRequest(request); } catch (EBaseException e) { - CMS.debug("EnrollProfile: Unable to update request after validation: " + e); - CMS.debug(e); + msg = method + " Unable to update request after validation: " + e; + CMS.debug(msg); + throw new EProfileException(msg); } - throw new EDeferException("defer request"); + } else if (popChallengeRequired) { + // this is encryptedPOP case; defer to require decryptedPOP + CMS.debug(method + " popChallengeRequired, defer to enforce decryptedPOP"); + validate(request); + CMS.debug(method + " about to call setPOPchallenge"); + try { + setPOPchallenge(request); + queue.updateRequest(request); + } catch (EBaseException e) { + msg = method + e; + CMS.debug(msg); + throw new EProfileException(msg); + } + + throw new EDeferException("EnrollProfile: submit: encryptedPOP defer request"); + } else { // this profile executes request that is authenticated // by non NoAuth - CMS.debug("EnrollProfile: auth token is not null"); + CMS.debug(method + " auth token is not null"); validate(request); execute(request); } @@ -377,14 +505,16 @@ public abstract class EnrollProfile extends BasicProfile public TaggedRequest[] parseCMC(Locale locale, String certreq) throws EProfileException { + + String method = "EnrollProfile: parseCMC: "; /* cert request must not be null */ if (certreq == null) { - CMS.debug("EnrollProfile: parseCMC() certreq null"); + CMS.debug(method + "certreq null"); throw new EProfileException( CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST")); } - //CMS.debug("EnrollProfile: Start parseCMC(): " + certreq); - CMS.debug("EnrollProfile.parseCMC(): start"); + //CMS.debug(method + " Start parseCMC(): " + certreq); + CMS.debug(method + "starts"); TaggedRequest msgs[] = null; @@ -416,6 +546,8 @@ public abstract class EnrollProfile extends BasicProfile if (numcontrols > 0) { context.put("numOfControls", Integer.valueOf(numcontrols)); TaggedAttribute[] attributes = new TaggedAttribute[numcontrols]; + boolean id_cmc_decryptedPOP = false; + SET decPopVals = null; boolean id_cmc_identification = false; SET ident = null; @@ -430,7 +562,11 @@ public abstract class EnrollProfile extends BasicProfile for (int i = 0; i < numcontrols; i++) { attributes[i] = (TaggedAttribute) controlSeq.elementAt(i); OBJECT_IDENTIFIER oid = attributes[i].getType(); - if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) { + if (oid.equals(OBJECT_IDENTIFIER.id_cmc_decryptedPOP)) { + CMS.debug(method + " decryptedPOP found"); + id_cmc_decryptedPOP = true; + decPopVals = attributes[i].getValues(); + } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) { id_cmc_identification = true; ident = attributes[i].getValues(); } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProofV2)) { @@ -460,6 +596,19 @@ public abstract class EnrollProfile extends BasicProfile */ UTF8String ident_s = null; + if (id_cmc_decryptedPOP && (decPopVals != null)) { + + DecryptedPOP decPop = (DecryptedPOP) (ASN1Util.decode(DecryptedPOP.getTemplate(), + ASN1Util.encode(decPopVals.elementAt(0)))); + CMS.debug(method + "DecryptedPOP encoded"); + + Integer reqId = verifyDecryptedPOP(decPop); + if (reqId != null) { + context.put("decryptedPopReqId", reqId); + } + return null; + } + if (id_cmc_identification) { ident_s = (UTF8String) (ASN1Util.decode(UTF8String.getTemplate(), ASN1Util.encode(ident.elementAt(0)))); @@ -523,16 +672,111 @@ public abstract class EnrollProfile extends BasicProfile } else return null; - CMS.debug("EnrollProfile.parseCMC: end"); + CMS.debug(method + "ends"); return msgs; } catch (Exception e) { - CMS.debug("EnrollProfile: Unable to parse CMC request: " + e); - CMS.debug(e); + CMS.debug(method + "Unable to parse CMC request: " + e); throw new EProfileException( CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e); } } + /** + * verifyDecryptedPOP verifies the POP challenge provided in + * DecryptedPOP and returns the matching requestID + * + * @author cfu + */ + private Integer verifyDecryptedPOP(DecryptedPOP decPop) { + String method = "EnrollProfile: verifyDecryptedPOP: "; + CMS.debug(method + "begins"); + + INTEGER iBody = decPop.getBodyPartID(); + CMS.debug(method + "request id from decryptedPOP =" + + iBody.toString()); + Integer reqId = new Integer(iBody.toString()); + + OCTET_STRING witness_os = decPop.getWitness(); + + IRequestQueue reqQueue = getRequestQueue(); + IRequest req = null; + try { + req = reqQueue.findRequest(new RequestId(reqId)); + } catch (Exception e) { + CMS.debug(method + e); + return null; + } + + // now verify the POP witness + byte[] pop_encreyptedData = req.getExtDataInByteArray("pop_encreyptedData"); + if (pop_encreyptedData == null) { + CMS.debug(method + + "pop_encreyptedData not found in request:" + reqId.toString()); + return null; + } + + byte[] pop_sysPubEncreyptedSession = req.getExtDataInByteArray("pop_sysPubEncreyptedSession"); + if (pop_sysPubEncreyptedSession == null) { + CMS.debug(method + + "pop_sysPubEncreyptedSession not found in request:" + reqId.toString()); + return null; + } + + byte[] cmc_msg = req.getExtDataInByteArray(IEnrollProfile.CTX_CERT_REQUEST); + if (pop_sysPubEncreyptedSession == null) { + CMS.debug(method + + "pop_sysPubEncreyptedSession not found in request:" + + reqId.toString()); + return null; + } + + ICertificateAuthority authority = (ICertificateAuthority) getAuthority(); + PrivateKey issuanceProtPrivKey = authority.getIssuanceProtPrivKey(); + if (issuanceProtPrivKey != null) + CMS.debug(method + "issuanceProtPrivKey not null"); + else + CMS.debug(method + "issuanceProtPrivKey null"); + + try { + CryptoToken token = null; + String tokenName = CMS.getConfigStore().getString("cmc.token", CryptoUtil.INTERNAL_TOKEN_NAME); + token = CryptoUtil.getKeyStorageToken(tokenName); + + SymmetricKey symKey = CryptoUtil.unwrap( + token, + SymmetricKey.Usage.DECRYPT, + issuanceProtPrivKey, + pop_sysPubEncreyptedSession); + byte[] challenge_b = CryptoUtil.decryptUsingSymmetricKey( + token, pop_encreyptedData, symKey); + if (challenge_b == null) { + CMS.debug(method + "decryptUsingSymmetricKey returned null"); + return null; + } + + MessageDigest digest = MessageDigest.getInstance(CryptoUtil.getDefaultHashAlgName()); + HMACDigest hmacDigest = new HMACDigest(digest, challenge_b); + hmacDigest.update(cmc_msg); + byte[] proofValue = hmacDigest.digest(); + boolean witnessChecked = Arrays.equals(proofValue, witness_os.toByteArray()); + if (!witnessChecked) { + CMS.debug(method + "POP challenge witness verification failure"); + return null; + } + } catch (Exception e) { + CMS.debug(method + e); + return null; + } + + CMS.debug(method + "POP challenge verified!"); + req.setExtData("cmc_POPchallengeRequired", "false"); + + CMS.debug(method + "cmc_POPchallengeRequired set back to false"); + CMS.debug(method + "ends"); + + return reqId; + } + private boolean verifyPOPLinkWitness(byte[] randomSeed, TaggedRequest req, SEQUENCE bpids) { ISharedToken tokenClass = null; @@ -711,7 +955,7 @@ public abstract class EnrollProfile extends BasicProfile * @param ident value of the id_cmc_identification control * @param reqSeq requestSequence of the PKI request PKIData * @return boolean true if the witness values correctly verified - * cfu + * @author cfu */ private boolean verifyIdentityProofV2( TaggedAttribute attr, @@ -769,8 +1013,8 @@ public abstract class EnrollProfile extends BasicProfile CMS.debug(method + " Failed to retrieve shared secret"); return false; } - // cfu REMOVE - CMS.debug(method + "Shared Secret returned by tokenClass:" + token); + + // CMS.debug(method + "Shared Secret returned by tokenClass:" + token); try { IdentityProofV2 idV2val = (IdentityProofV2) (ASN1Util.decode(IdentityProofV2.getTemplate(), ASN1Util.encode(vals.elementAt(0)))); @@ -851,16 +1095,17 @@ public abstract class EnrollProfile extends BasicProfile public void fillTaggedRequest(Locale locale, TaggedRequest tagreq, X509CertInfo info, IRequest req) throws EProfileException { + String method = "EnrollProfile: fillTaggedRequest: "; TaggedRequest.Type type = tagreq.getType(); if (type == null) { - CMS.debug("EnrollProfile: fillTaggedRequest: TaggedRequest type == null"); + CMS.debug(method + "TaggedRequest type == null"); throw new EProfileException( CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST")+ "TaggedRequest type null"); } if (type.equals(TaggedRequest.PKCS10)) { - CMS.debug("EnrollProfile: fillTaggedRequest: TaggedRequest type == pkcs10"); + CMS.debug(method + " TaggedRequest type == pkcs10"); boolean sigver = true; boolean tokenSwitched = false; CryptoManager cm = null; @@ -890,36 +1135,73 @@ public abstract class EnrollProfile extends BasicProfile req.setExtData("bodyPartId", tcr.getBodyPartID()); fillPKCS10(locale, pkcs10, info, req); } catch (Exception e) { - CMS.debug("EnrollProfile: fillTaggedRequest " + e); + CMS.debug(method + e); } finally { if ((sigver == true) && (tokenSwitched == true)){ cm.setThreadToken(savedToken); } } } else if (type.equals(TaggedRequest.CRMF)) { - CMS.debug("EnrollProfile: fillTaggedRequest: TaggedRequest type == crmf"); + CMS.debug(method + " TaggedRequest type == crmf"); CertReqMsg crm = tagreq.getCrm(); SessionContext context = SessionContext.getContext(); Integer nums = (Integer) (context.get("numOfControls")); - // check if the LRA POP Witness Control attribute exists - if (nums != null && nums.intValue() > 0) { - TaggedAttribute attr = - (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_lraPOPWitness)); - if (attr != null) { - parseLRAPopWitness(locale, crm, attr); + boolean verifyAllow = false; //disable RA by default + try { + String configName = "cmc.lraPopWitness.verify.allow"; + CMS.debug(method + "getting :" + configName); + verifyAllow = CMS.getConfigStore().getBoolean(configName, false); + CMS.debug(method + "cmc.lraPopWitness.verify.allow is " + verifyAllow); + } catch (Exception e) { + // unlikely to get here + String msg = method + " Failed to retrieve cmc.lraPopWitness.verify.allow"; + CMS.debug(msg); + throw new EProfileException(method + msg); + } + if (verifyAllow) { + // check if the LRA POP Witness Control attribute exists + if (nums != null && nums.intValue() > 0) { + TaggedAttribute attr = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_lraPOPWitness)); + if (attr != null) { + parseLRAPopWitness(locale, crm, attr); + } else { + CMS.debug( + method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); + if (crm.hasPop()) { + CMS.debug(method + " hasPop true"); + verifyPOP(locale, crm); + } else { // no signing POP, then do it the hard way + CMS.debug(method + "hasPop false, need to challenge"); + req.setExtData("cmc_POPchallengeRequired", "true"); + } + } } else { - CMS.debug("EnrollProfile: verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); + CMS.debug( + method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); + if (crm.hasPop()) { + CMS.debug(method + " hasPop true"); + verifyPOP(locale, crm); + } else { // no signing POP, then do it the hard way + CMS.debug(method + "hasPop false, need to challenge"); + req.setExtData("cmc_POPchallengeRequired", "true"); + } + } + + } else { //!verifyAllow + + if (crm.hasPop()) { + CMS.debug(method + " hasPop true"); verifyPOP(locale, crm); + } else { // no signing POP, then do it the hard way + CMS.debug(method + "hasPop false, need to challenge"); + req.setExtData("cmc_POPchallengeRequired", "true"); } - } else { - CMS.debug("EnrollProfile: verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); - verifyPOP(locale, crm); } fillCertReqMsg(locale, crm, info, req); } else { - CMS.debug("EnrollProfile: fillTaggedRequest: unsupported type (not CRMF or PKCS10)"); + CMS.debug(method + " unsupported type (not CRMF or PKCS10)"); throw new EProfileException( CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST")); } @@ -1648,25 +1930,30 @@ public abstract class EnrollProfile extends BasicProfile return profileID; } + /* + * verifyPOP - CRMF POP verification for signing keys + */ public void verifyPOP(Locale locale, CertReqMsg certReqMsg) throws EProfileException { - CMS.debug("EnrollProfile ::in verifyPOP"); + String method = "EnrollProfile: verifyPOP: "; + CMS.debug(method + "for signing keys"); String auditMessage = null; String auditSubjectID = auditSubjectID(); if (!certReqMsg.hasPop()) { - return; + CMS.debug(method + "missing pop."); + popFailed(locale, auditSubjectID, auditMessage); } ProofOfPossession pop = certReqMsg.getPop(); ProofOfPossession.Type popType = pop.getType(); if (popType != ProofOfPossession.SIGNATURE) { - return; + CMS.debug(method + "pop type is not ProofOfPossession.SIGNATURE."); + popFailed(locale, auditSubjectID, auditMessage); } try { - CryptoManager cm = CryptoManager.getInstance(); CryptoToken verifyToken = null; String tokenName = CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME); if (CryptoUtil.isInternalToken(tokenName)) { @@ -1685,9 +1972,18 @@ public abstract class EnrollProfile extends BasicProfile ILogger.SUCCESS); audit(auditMessage); } catch (Exception e) { + CMS.debug(method + "Unable to verify POP: " + e); + popFailed(locale, auditSubjectID, auditMessage, e); + } + CMS.debug(method + "ends."); + } - CMS.debug("EnrollProfile: Unable to verify POP: " + e); - CMS.debug(e); + private void popFailed(Locale locale, String auditSubjectID, String auditMessage) + throws EProfileException { + popFailed(locale, auditSubjectID, auditMessage, null); + } + private void popFailed(Locale locale, String auditSubjectID, String auditMessage, Exception e) + throws EProfileException { // store a message in the signed audit log file auditMessage = CMS.getLogMessage( @@ -1697,8 +1993,12 @@ public abstract class EnrollProfile extends BasicProfile audit(auditMessage); - throw new EProfileException(CMS.getUserMessage(locale, + if (e != null) { + throw new EProfileException(CMS.getUserMessage(locale, "CMS_POP_VERIFICATION_ERROR"), e); - } + } else { + throw new EProfileException(CMS.getUserMessage(locale, + "CMS_POP_VERIFICATION_ERROR")); + } } } diff --git a/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java b/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java index f1320f44a..f5bef2a87 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java +++ b/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java @@ -30,16 +30,6 @@ import java.util.Hashtable; import javax.servlet.http.HttpServletResponse; -import netscape.security.x509.CRLExtensions; -import netscape.security.x509.CRLReasonExtension; -import netscape.security.x509.CertificateChain; -import netscape.security.x509.InvalidityDateExtension; -import netscape.security.x509.RevocationReason; -import netscape.security.x509.RevokedCertImpl; -import netscape.security.x509.X500Name; -import netscape.security.x509.X509CertImpl; -import netscape.security.x509.X509Key; - import org.mozilla.jss.CryptoManager; import org.mozilla.jss.asn1.ANY; import org.mozilla.jss.asn1.ASN1Util; @@ -58,15 +48,18 @@ import org.mozilla.jss.pkcs11.PK11PubKey; import org.mozilla.jss.pkix.cert.Certificate; import org.mozilla.jss.pkix.cmc.CMCCertId; import org.mozilla.jss.pkix.cmc.CMCStatusInfo; +import org.mozilla.jss.pkix.cmc.EncryptedPOP; import org.mozilla.jss.pkix.cmc.GetCert; import org.mozilla.jss.pkix.cmc.OtherInfo; import org.mozilla.jss.pkix.cmc.OtherMsg; import org.mozilla.jss.pkix.cmc.PendInfo; import org.mozilla.jss.pkix.cmc.ResponseBody; import org.mozilla.jss.pkix.cmc.TaggedAttribute; +import org.mozilla.jss.pkix.cmc.TaggedRequest; import org.mozilla.jss.pkix.cmmf.RevRequest; import org.mozilla.jss.pkix.cms.ContentInfo; import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo; +import org.mozilla.jss.pkix.cms.EnvelopedData; import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber; import org.mozilla.jss.pkix.cms.SignedData; import org.mozilla.jss.pkix.cms.SignerIdentifier; @@ -89,6 +82,17 @@ import com.netscape.certsrv.request.IRequest; import com.netscape.certsrv.request.IRequestQueue; import com.netscape.certsrv.request.RequestId; import com.netscape.certsrv.request.RequestStatus; +import com.netscape.cmsutil.crypto.CryptoUtil; + +import netscape.security.x509.CRLExtensions; +import netscape.security.x509.CRLReasonExtension; +import netscape.security.x509.CertificateChain; +import netscape.security.x509.InvalidityDateExtension; +import netscape.security.x509.RevocationReason; +import netscape.security.x509.RevokedCertImpl; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509Key; /** * Utility CMCOutputTemplate @@ -142,6 +146,10 @@ public class CMCOutputTemplate { public void createFullResponse(HttpServletResponse resp, IRequest[] reqs, String cert_request_type, int[] error_codes) { + String method = "CMCOutputTemplate: createFullResponse: "; + CMS.debug(method + + "begins with cert_request_type=" + + cert_request_type); SEQUENCE controlSeq = new SEQUENCE(); SEQUENCE cmsSeq = new SEQUENCE(); @@ -176,22 +184,33 @@ public class CMCOutputTemplate { OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo); controlSeq.addElement(tagattr); } else if (cert_request_type.equals("cmc")) { + CMS.debug(method + " processing cmc"); pending_bpids = new SEQUENCE(); success_bpids = new SEQUENCE(); failed_bpids = new SEQUENCE(); + EncryptedPOP encPop = null; if (reqs != null) { for (int i = 0; i < reqs.length; i++) { + CMS.debug(method + " error_codes[i]=" + error_codes[i]); if (error_codes[i] == 0) { success_bpids.addElement(new INTEGER( reqs[i].getExtDataInBigInteger("bodyPartId"))); } else if (error_codes[i] == 2) { pending_bpids.addElement(new INTEGER( reqs[i].getExtDataInBigInteger("bodyPartId"))); + try { + encPop = constructEncryptedPop(reqs[i]); + } catch (Exception e) { + CMS.debug(method + e); + return; + } } else { failed_bpids.addElement(new INTEGER( reqs[i].getExtDataInBigInteger("bodyPartId"))); } } + } else { + CMS.debug(method + " reqs null. why?"); } TaggedAttribute tagattr = null; @@ -221,8 +240,25 @@ public class CMCOutputTemplate { } if (pending_bpids.size() > 0) { + // handle encryptedPOP control first + + if (encPop != null) { + CMS.debug(method + "adding encPop"); + tagattr = new TaggedAttribute( + new INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_encryptedPOP, + encPop); + controlSeq.addElement(tagattr); + CMS.debug(method + "encPop added"); + } + + String reqId = reqs[0].getRequestId().toString(); + OtherInfo otherInfo = null; + PendInfo pendInfo = new PendInfo(reqId, new Date()); + otherInfo = new OtherInfo(OtherInfo.PEND, null, + pendInfo); cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.PENDING, - pending_bpids, (String) null, null); + pending_bpids, (String) null, otherInfo); tagattr = new TaggedAttribute( new INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo); @@ -238,7 +274,7 @@ public class CMCOutputTemplate { } catch (Exception e) { } if (confirmRequired) { - CMS.debug("CMCOutputTemplate: confirmRequired in the request"); + CMS.debug(method + " confirmRequired in the request"); cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.CONFIRM_REQUIRED, success_bpids, (String) null, null); @@ -270,13 +306,14 @@ public class CMCOutputTemplate { // deal with controls Integer nums = (Integer) (context.get("numOfControls")); if (nums != null && nums.intValue() > 0) { + CMS.debug(method + " processing controls"); TaggedAttribute attr = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_getCert)); if (attr != null) { try { processGetCertControl(attr, certs); } catch (EBaseException ee) { - CMS.debug("CMCOutputTemplate: " + ee.toString()); + CMS.debug(method + ee.toString()); OtherInfo otherInfo1 = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_CERT_ID), null); SEQUENCE bpids1 = new SEQUENCE(); @@ -342,6 +379,10 @@ public class CMCOutputTemplate { ResponseBody respBody = new ResponseBody(controlSeq, cmsSeq, otherMsgSeq); + if (respBody != null) + CMS.debug(method + " after new ResponseBody, respBody not null"); + else + CMS.debug(method + " after new ResponseBody, respBody null"); ContentInfo contentInfo = getContentInfo(respBody, certs); ByteArrayOutputStream fos = new ByteArrayOutputStream(); @@ -354,18 +395,79 @@ public class CMCOutputTemplate { OutputStream os = resp.getOutputStream(); os.write(contentBytes); os.flush(); + CMS.debug(method + "ends"); } catch (java.security.cert.CertificateEncodingException e) { - CMS.debug("CMCOutputTemplate exception: " + e.toString()); + CMS.debug(method + e.toString()); } catch (InvalidBERException e) { - CMS.debug("CMCOutputTemplate exception: " + e.toString()); + CMS.debug(method + e.toString()); } catch (IOException e) { - CMS.debug("CMCOutputTemplate exception: " + e.toString()); + CMS.debug(method + e.toString()); } catch (Exception e) { - CMS.debug("Exception: " + e.toString()); + CMS.debug(method + e.toString()); + } + } + + /** + * constructEncryptedPop pulls cmc pop challenge fields out of the request + * and constructs an EncryptedPOP + * to be included in the response later + * + * @author cfu + */ + public EncryptedPOP constructEncryptedPop(IRequest req) + throws EBaseException { + String method = "CMCOutputTemplate: constructEncryptedPop: "; + CMS.debug(method + "begins"); + EncryptedPOP encPop = null; + + boolean popChallengeRequired = req.getExtDataInBoolean("cmc_POPchallengeRequired", false); + if (!popChallengeRequired) { + CMS.debug(method + "popChallengeRequired false"); + return null; } + CMS.debug(method + "popChallengeRequired true"); + byte[] cmc_msg = req.getExtDataInByteArray(IEnrollProfile.CTX_CERT_REQUEST); + byte[] pop_encreyptedData = req.getExtDataInByteArray("pop_encreyptedData"); + //don't need this for encryptedPOP, but need to check for existence anyway + byte[] pop_sysPubEncreyptedSession = req.getExtDataInByteArray("pop_sysPubEncreyptedSession"); + byte[] pop_userPubEncreyptedSession = req.getExtDataInByteArray("pop_userPubEncreyptedSession"); + if ((pop_encreyptedData != null) && + (pop_sysPubEncreyptedSession != null) && + (pop_userPubEncreyptedSession != null)) { + // generate encryptedPOP here + // algs are hard-coded for now + + try { + EnvelopedData envData = CryptoUtil.createEnvelopedData( + pop_encreyptedData, + pop_userPubEncreyptedSession); + ContentInfo ci = new ContentInfo(envData); + CMS.debug(method + "now we can compose encryptedPOP"); + + TaggedRequest.Template tReqTemplate = new TaggedRequest.Template(); + TaggedRequest tReq = (TaggedRequest) tReqTemplate.decode( + new ByteArrayInputStream(cmc_msg)); + + encPop = new EncryptedPOP( + tReq, + ci, + CryptoUtil.getDefaultEncAlg(), + CryptoUtil.getDefaultHashAlg(), + new OCTET_STRING(req.getExtDataInByteArray("pop_witness"))); + + } catch (Exception e) { + CMS.debug(method + " excepton:" + e); + throw new EBaseException(method + " excepton:" + e); + } + + } + + return encPop; } private ContentInfo getContentInfo(ResponseBody respBody, SET certs) { + String method = "CMCOutputTemplate: getContentInfo: "; + CMS.debug(method + "begins"); try { ICertificateAuthority ca = null; // add CA cert chain @@ -442,10 +544,10 @@ public class CMCOutputTemplate { enContentInfo, certs, null, signInfos); ContentInfo contentInfo = new ContentInfo(signedData); - CMS.debug("CMCOutputTemplate::getContentInfo() - done"); + CMS.debug(method + " - done"); return contentInfo; } catch (Exception e) { - CMS.debug("CMCOutputTemplate: Failed to create CMCContentInfo. Exception: " + e.toString()); + CMS.debug(method + " Failed to create CMCContentInfo. Exception: " + e.toString()); } return null; } diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java index 619103190..c233e415b 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java +++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java @@ -28,8 +28,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import netscape.security.x509.X509CertImpl; - import org.mozilla.jss.asn1.ASN1Util; import org.mozilla.jss.asn1.INTEGER; import org.mozilla.jss.asn1.InvalidBERException; @@ -58,6 +56,7 @@ import com.netscape.certsrv.profile.IProfileInput; import com.netscape.certsrv.profile.IProfileSubsystem; import com.netscape.certsrv.request.INotify; import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.request.RequestId; import com.netscape.certsrv.request.RequestStatus; import com.netscape.cms.servlet.common.AuthCredentials; import com.netscape.cms.servlet.common.CMCOutputTemplate; @@ -65,6 +64,8 @@ import com.netscape.cms.servlet.common.CMSRequest; import com.netscape.cms.servlet.common.CMSTemplate; import com.netscape.cmsutil.util.Utils; +import netscape.security.x509.X509CertImpl; + /** * This servlet submits end-user request into the profile framework. * @@ -478,12 +479,13 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { TaggedAttribute attr = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_lraPOPWitness)); if (attr != null) { - boolean verifyAllow = true; + boolean verifyAllow = false; //disable RA by default try { verifyAllow = CMS.getConfigStore().getBoolean( - "cmc.lraPopWitness.verify.allow", true); + "cmc.lraPopWitness.verify.allow", false); } catch (EBaseException ee) { } + CMS.debug("ProfileSubmitCMCServlet: cmc.lraPopWitness.verify.allow is " + verifyAllow); if (!verifyAllow) { LraPopWitness lraPop = null; @@ -507,23 +509,34 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { } } - // for CMC, requests may be zero. Then check if controls exist. + // For CMC, requests may be zero. Then check if controls exist. + // In case of decryptedPOP, request already exists, find it and + // put in provedReq. + IRequest provedReq = null; if (reqs == null) { - Integer nums = (Integer) (context.get("numOfControls")); - CMCOutputTemplate template = new CMCOutputTemplate(); - // if there is only one control GetCert, then simple response - // must be returned. - if (nums != null && nums.intValue() == 1) { - TaggedAttribute attr1 = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_getCert)); - if (attr1 != null) { - template.createSimpleResponse(response, reqs); + // handling DecryptedPOP request here + Integer reqID = (Integer) context.get("decryptedPopReqId"); + provedReq = profile.getRequestQueue().findRequest(new RequestId(reqID.toString())); + if (provedReq == null) { + + Integer nums = (Integer) (context.get("numOfControls")); + CMCOutputTemplate template = new CMCOutputTemplate(); + // if there is only one control GetCert, then simple response + // must be returned. + if (nums != null && nums.intValue() == 1) { + TaggedAttribute attr1 = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_getCert)); + if (attr1 != null) { + template.createSimpleResponse(response, reqs); + } else + template.createFullResponse(response, reqs, + cert_request_type, null); } else template.createFullResponse(response, reqs, cert_request_type, null); - } else - template.createFullResponse(response, reqs, - cert_request_type, null); - return; + return; + } else { + CMS.debug("ProfileSubmitCMCServlet: provedReq not null"); + } } String errorCode = null; @@ -532,7 +545,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { /////////////////////////////////////////////// // populate request /////////////////////////////////////////////// - for (int k = 0; k < reqs.length; k++) { + for (int k = 0; (provedReq == null) &&(k < reqs.length); k++) { // adding parameters to request setInputsIntoRequest(request, profile, reqs[k]); @@ -626,7 +639,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { OtherInfo.INTERNAL_CA_ERROR, s); return; } - } + } //for String auditMessage = null; String auditSubjectID = auditSubjectID(); @@ -640,7 +653,8 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { int error_codes[] = null; if (reqs != null && reqs.length > 0) error_codes = new int[reqs.length]; - for (int k = 0; k < reqs.length; k++) { + + for (int k = 0; (provedReq == null) && (k < reqs.length); k++) { try { // reset the "auditRequesterID" auditRequesterID = auditRequesterID(reqs[k]); @@ -680,6 +694,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { } } catch (EDeferException e) { // return defer message to the user + CMS.debug("ProfileSubmitCMCServlet: set request to PENDING"); reqs[k].setRequestStatus(RequestStatus.PENDING); // need to notify INotify notify = profile.getRequestQueue().getPendingNotify(); @@ -752,6 +767,53 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { error_codes[k] = 0; } + // handle provedReq + if (provedReq != null) { + error_codes = new int[1]; + auditRequesterID = auditRequesterID(provedReq); + try { + profile.execute(provedReq); + reqs = new IRequest[1]; + reqs[0] = provedReq; + reqs[0].setRequestStatus(RequestStatus.COMPLETE); + profile.getRequestQueue().markAsServiced(provedReq); + CMS.debug("ProfileSubmitCMCServlet: provedReq set to complete"); + + // reset the "auditInfoCertValue" + auditInfoCertValue = auditInfoCertValue(reqs[0]); + + if (auditInfoCertValue != null) { + if (!(auditInfoCertValue.equals( + ILogger.SIGNED_AUDIT_EMPTY_VALUE))) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CERT_REQUEST_PROCESSED, + auditSubjectID, + ILogger.SUCCESS, + auditRequesterID, + ILogger.SIGNED_AUDIT_ACCEPTANCE, + auditInfoCertValue); + + audit(auditMessage); + } + } + } catch (ERejectException e) { + // return error to the user + provedReq.setRequestStatus(RequestStatus.REJECTED); + CMS.debug("ProfileSubmitCMCServlet: provedReq submit " + e.toString()); + errorCode = "3"; + errorReason = CMS.getUserMessage(locale, + "CMS_PROFILE_REJECTED", + e.toString()); + } catch (Exception e) { + // return error to the user + CMS.debug("ProfileSubmitCMCServlet: provedReq submit " + e.toString()); + errorCode = "1"; + errorReason = CMS.getUserMessage(locale, + "CMS_INTERNAL_ERROR"); + } + } + if (errorCode != null) { // create the CMC full enrollment response CMCOutputTemplate template = new CMCOutputTemplate(); diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java index e3a378ebc..716a3f23f 100644 --- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java +++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java @@ -55,10 +55,13 @@ import org.mozilla.jss.asn1.ANY; import org.mozilla.jss.asn1.ASN1Util; import org.mozilla.jss.asn1.ASN1Value; import org.mozilla.jss.asn1.BIT_STRING; +import org.mozilla.jss.asn1.INTEGER; import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.NULL; import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; import org.mozilla.jss.asn1.OCTET_STRING; import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; import org.mozilla.jss.crypto.Algorithm; import org.mozilla.jss.crypto.BadPaddingException; import org.mozilla.jss.crypto.Cipher; @@ -89,7 +92,11 @@ import org.mozilla.jss.crypto.X509Certificate; import org.mozilla.jss.pkcs11.PK11ECPublicKey; import org.mozilla.jss.pkcs11.PK11PubKey; import org.mozilla.jss.pkcs12.PasswordConverter; -import org.mozilla.jss.pkcs7.EncryptedContentInfo; +import org.mozilla.jss.pkcs7.IssuerAndSerialNumber; +import org.mozilla.jss.pkcs7.RecipientInfo; +import org.mozilla.jss.pkix.cms.ContentInfo; +import org.mozilla.jss.pkix.cms.EncryptedContentInfo; +import org.mozilla.jss.pkix.cms.EnvelopedData; import org.mozilla.jss.pkix.crmf.CertReqMsg; import org.mozilla.jss.pkix.crmf.CertRequest; import org.mozilla.jss.pkix.crmf.CertTemplate; @@ -2391,6 +2398,41 @@ public class CryptoUtil { } /** + * for CMC encryptedPOP + */ + public static EnvelopedData createEnvelopedData(byte[] encContent, byte[] encSymKey) + throws Exception { + String method = "CryptoUtl: createEnvelopedData: "; + System.out.println(method + "begins"); + + EncryptedContentInfo encCInfo = new EncryptedContentInfo( + ContentInfo.DATA, + getDefaultEncAlg(), + new OCTET_STRING(encContent)); + + Name name = new Name(); + name.addCommonName("unUsedIssuerName"); //unused; okay for cmc EncryptedPOP + RecipientInfo recipient = new RecipientInfo( + new INTEGER(0), //per rfc2315 + new IssuerAndSerialNumber(name, new INTEGER(0)), //unUsed + new AlgorithmIdentifier(RSA_ENCRYPTION, new NULL()), + new OCTET_STRING(encSymKey)); + + SET recipients = new SET(); + recipients.addElement(recipient); + + EnvelopedData envData = new EnvelopedData( + new INTEGER(0), + recipients, + encCInfo); + + return envData; + } + + /* PKCS 1 - rsaEncryption */ + public static OBJECT_IDENTIFIER RSA_ENCRYPTION = new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 1, 1, 1 }); + + /** * The following are convenience routines for quick preliminary * feature development or test programs that would just take * the defaults @@ -2538,6 +2580,32 @@ public class CryptoUtil { } return oid; } + + /** + * getNameFromHashAlgorithm returns the hashing algorithm name + * from input Algorithm + * + * @param ai the hashing algorithm AlgorithmIdentifier + * @return name of the hashing algorithm + * + */ + public static String getNameFromHashAlgorithm(AlgorithmIdentifier ai) + throws NoSuchAlgorithmException { + OBJECT_IDENTIFIER oid = null; + + System.out.println("CryptoUtil: getNameFromHashAlgorithm: " + ai.getOID().toString()); + if (ai != null) { + if (ai.getOID().equals((DigestAlgorithm.SHA256).toOID())) { + return "SHA-256"; + } else if (ai.getOID().equals((DigestAlgorithm.SHA384).toOID())) { + return "SHA-384"; + } else if (ai.getOID().equals((DigestAlgorithm.SHA512).toOID())) { + return "SHA-512"; + } + } + throw new NoSuchAlgorithmException(); + } + } // START ENABLE_ECC |