diff options
Diffstat (limited to 'base')
4 files changed, 388 insertions, 38 deletions
diff --git a/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java b/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java index 830c8866e..84b024404 100644 --- a/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java +++ b/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java @@ -26,6 +26,9 @@ import org.mozilla.jss.pkix.cmc.PKIData; */ public interface ISharedToken { + // support for id_cmc_identification + public String getSharedToken(String identification); + public String getSharedToken(PKIData cmcData); public String getSharedToken(BigInteger serialnum); diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java index c51808212..bca569bab 100644 --- a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java +++ b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java @@ -21,6 +21,7 @@ import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.CharConversionException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -54,6 +55,7 @@ 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.GetCert; +import org.mozilla.jss.pkix.cmc.IdentityProofV2; import org.mozilla.jss.pkix.cmc.LraPopWitness; import org.mozilla.jss.pkix.cmc.OtherMsg; import org.mozilla.jss.pkix.cmc.PKIData; @@ -144,7 +146,11 @@ public class CMCRequest { */ static ContentInfo getCMCBlob(X509Certificate signerCert, String tokenName, String nickname, String[] rValue, String format, CryptoManager manager, String transactionMgtEnable, - String transactionMgtId, String identityProofEnable, String identityProofSharedSecret, + 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"); @@ -228,9 +234,21 @@ public class CMCRequest { bpid = addTransactionAttr(bpid, controlSeq, transactionMgtId, format, pkcs, certReqMsg); - if (identityProofEnable.equals("true")) + if (identificationEnable.equals("true")) { + bpid = addIdentificationAttr(bpid, controlSeq, identification); + } + + // for identityProof, it's either V2 or not V2; can't be both + // if both, V2 takes precedence + if (identityProofV2Enable.equals("true")) { + bpid = addIdentityProofV2Attr(bpid, controlSeq, reqSequence, + identityProofV2SharedSecret, + (identificationEnable.equals("true")) ? identification : null, + identityProofV2hashAlg, identityProofV2macAlg); + } else if (identityProofEnable.equals("true")) { bpid = addIdentityProofAttr(bpid, controlSeq, reqSequence, identityProofSharedSecret); + } PKIData pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq); @@ -416,9 +434,29 @@ public class CMCRequest { System.out.println("# is present."); System.out.println("revRequest.invalidityDatePresent=false"); System.out.println(""); + System.out.println("#identityProofV2.enable: if true, then the request will contain"); + System.out.println("#this control. Otherwise, false."); + 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("#Supported macAlg are:"); + System.out.println("# SHA-1-HMAC, 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(""); + System.out.println("#identification works with identityProofV2"); + System.out.println("identification.enable=false"); + System.out.println("identification=testuser"); + System.out.println(""); System.out.println("#identityProof.enable: if true, then the request will contain"); System.out.println("#this control. Otherwise, false."); - System.out.println("identityProof.enable=true"); + System.out.println("#Note that this control is updated by identityProofV2 above"); + System.out.println("identityProof.enable=false"); System.out.println(""); System.out.println("#identityProof.sharedSecret: Shared Secret"); System.out.println("identityProof.sharedSecret=testing"); @@ -503,6 +541,90 @@ public class CMCRequest { return RevRequest.unspecified; } + /** + * add IdentityProofV2 to the control sequence + * + * @param bpid Body part id + * @param seq control sequence + * @param reqSequence request sequence + * @param sharedSecret shared secret + * @param hashAlgString hash algorithm + * @param macAlgString mac algorithm + * cfu + */ + private static int addIdentityProofV2Attr(int bpid, + SEQUENCE seq, SEQUENCE reqSequence, + String sharedSecret, + String ident, + String hashAlgString, String macAlgString) { + String method = "CMCRequest: addIdentityProofV2Attr: "; + byte[] b = ASN1Util.encode(reqSequence); + byte[] key = null; + byte[] finalDigest = null; + + // default to SHA256 if not specified + if (hashAlgString == null) { + hashAlgString = "SHA-256"; + } + if (macAlgString == null) { + macAlgString = "SHA-256-HMAC"; + } + System.out.println(method + "hashAlg=" + hashAlgString + + "; macAlg=" + macAlgString); + + String toBeDigested = sharedSecret; + if (ident != null) { + toBeDigested = sharedSecret + ident; + } + try { + MessageDigest hash = MessageDigest.getInstance(hashAlgString); + key = hash.digest(toBeDigested.getBytes()); + } catch (NoSuchAlgorithmException ex) { + System.out.println(method + "No such algorithm!"); + return -1; + } + + MessageDigest mac; + try { + mac = MessageDigest.getInstance(CryptoUtil.getHMACtoMessageDigestName(macAlgString)); + HMACDigest hmacDigest = new HMACDigest(mac, key); + hmacDigest.update(b); + finalDigest = hmacDigest.digest(); + } catch (NoSuchAlgorithmException ex) { + System.out.println(method + "No such algorithm!"); + return -1; + } + + AlgorithmIdentifier hashAlg; + try { + hashAlg = new AlgorithmIdentifier(CryptoUtil.getHashAlgorithmOID(hashAlgString)); + } catch (NoSuchAlgorithmException ex) { + System.out.println(method + "No such hashing algorithm:" + hashAlgString); + return -1; + } + AlgorithmIdentifier macAlg; + try { + macAlg = new AlgorithmIdentifier(CryptoUtil.getHMACAlgorithmOID(macAlgString)); + } catch (NoSuchAlgorithmException ex) { + System.out.println(method + "No such HMAC algorithm:" + macAlgString); + return -1; + } + IdentityProofV2 idV2val = new IdentityProofV2(hashAlg, macAlg, new OCTET_STRING(finalDigest)); + TaggedAttribute identityProofV2 = new TaggedAttribute(new INTEGER(bpid++), + OBJECT_IDENTIFIER.id_cmc_identityProofV2, + idV2val); + seq.addElement(identityProofV2); + System.out.println("Identity Proof V2 control: "); + System.out.print(" Value: "); + for (int i = 0; i < finalDigest.length; i++) { + System.out.print(finalDigest[i] + " "); + } + System.out.println(""); + System.out.println("Successfully create identityProofV2 control. bpid = " + (bpid - 1)); + System.out.println(""); + return bpid; + } + private static int addIdentityProofAttr(int bpid, SEQUENCE seq, SEQUENCE reqSequence, String sharedSecret) { byte[] b = ASN1Util.encode(reqSequence); @@ -797,6 +919,40 @@ public class CMCRequest { return bpid; } + /** + * addIdentificationAttr adds the identification control + * + * @param bpid + * @param seq + * @param ident + * @return + * cfu + */ + private static int addIdentificationAttr(int bpid, SEQUENCE seq, String ident) { + UTF8String ident_s = null; + if (ident == null) { + System.out.println("Error in creating identification control: identification null"); + System.exit(1); + } else { + System.out.println("identification control: identification =" + ident); + } + + try { + if (ident.length() > 0) + ident_s = new UTF8String(ident); + } catch (CharConversionException e) { + System.out.println("Error in creating identification control:" + e.toString()); + System.exit(1); + } + + TaggedAttribute identVal = new TaggedAttribute(new INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_identification, + ident_s); + System.out.println("Successfully create identification control. bpid = " + (bpid - 1)); + System.out.println(""); + seq.addElement(identVal); + return bpid; + } + private static int addPopLinkWitnessAttr(int bpid, SEQUENCE controlSeq) { byte[] seed = { 0x10, 0x53, 0x42, 0x24, 0x1a, 0x2a, 0x35, 0x3c, @@ -831,7 +987,9 @@ public class CMCRequest { String revRequestEnable = "false", revRequestIssuer = null, revRequestSerial = null; String revRequestReason = null, revRequestSharedSecret = null, revRequestComment = null; String revRequestInvalidityDatePresent = "false"; + String identificationEnable = "false", identification = null; String identityProofEnable = "false", identityProofSharedSecret = null; + String identityProofV2Enable = "false", identityProofV2SharedSecret = null, identityProofV2hashAlg = "SHA256", identityProofV2macAlg = "SHA256"; String popLinkWitnessEnable = "false"; String bodyPartIDs = null, lraPopWitnessEnable = "false"; @@ -928,6 +1086,18 @@ public class CMCRequest { revRequestInvalidityDatePresent = val; } else if (name.equals("revRequest.nickname")) { revCertNickname = val; + } else if (name.equals("identification.enable")) { + identificationEnable = val; + } else if (name.equals("identification")) { + identification = val; + } else if (name.equals("identityProofV2.enable")) { + identityProofV2Enable = val; + } else if (name.equals("identityProofV2.sharedSecret")) { + identityProofV2SharedSecret = val; + } else if (name.equals("identityProofV2.hashAlg")) { + identityProofV2hashAlg = val; + } else if (name.equals("identityProofV2.macAlg")) { + identityProofV2macAlg = val; } else if (name.equals("identityProof.enable")) { identityProofEnable = val; } else if (name.equals("identityProof.sharedSecret")) { @@ -1148,8 +1318,12 @@ public class CMCRequest { } ContentInfo cmcblob = getCMCBlob(signerCert, tokenName, nickname, requests, format, - cm, transactionMgtEnable, transactionMgtId, identityProofEnable, - identityProofSharedSecret, controlSeq, otherMsgSeq, bpid); + cm, transactionMgtEnable, transactionMgtId, + identificationEnable, identification, + identityProofEnable, identityProofSharedSecret, + identityProofV2Enable, identityProofV2SharedSecret, + identityProofV2hashAlg, identityProofV2macAlg, + controlSeq, otherMsgSeq, bpid); // (6) Finally, print the actual CMC blob to the // specified output file diff --git a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java index 48a23536a..736110731 100644 --- a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java +++ b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java @@ -28,6 +28,11 @@ public class SharedSecret implements ISharedToken { public SharedSecret() { } + // support for id_cmc_identification + public String getSharedToken(String identification) { + return "testing"; + } + public String getSharedToken(PKIData cmcdata) { return "testing"; } @@ -36,7 +41,4 @@ public class SharedSecret implements ISharedToken { return "testing"; } - public String getSharedToken(String identification) { - return "testing"; - } } 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 8d10ec26b..972d89f6a 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 @@ -39,9 +39,13 @@ 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.asn1.UTF8String; import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.DigestAlgorithm; +import org.mozilla.jss.crypto.HMACAlgorithm; import org.mozilla.jss.pkcs10.CertificationRequest; import org.mozilla.jss.pkcs10.CertificationRequestInfo; +import org.mozilla.jss.pkix.cmc.IdentityProofV2; import org.mozilla.jss.pkix.cmc.LraPopWitness; import org.mozilla.jss.pkix.cmc.OtherMsg; import org.mozilla.jss.pkix.cmc.PKIData; @@ -412,28 +416,80 @@ public abstract class EnrollProfile extends BasicProfile if (numcontrols > 0) { context.put("numOfControls", Integer.valueOf(numcontrols)); TaggedAttribute[] attributes = new TaggedAttribute[numcontrols]; + + boolean id_cmc_identification = false; + SET ident = null; + + boolean id_cmc_identityProofV2 = false; + boolean id_cmc_identityProof = false; + TaggedAttribute attr = null; + + boolean id_cmc_idPOPLinkRandom = false; + SET vals = null; + 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_identityProof)) { - boolean valid = verifyIdentityProof(attributes[i], - reqSeq); - if (!valid) { - SEQUENCE bpids = getRequestBpids(reqSeq); - context.put("identityProof", bpids); - return null; - } + 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)) { + id_cmc_identityProofV2 = true; + attr = attributes[i]; + } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProof)) { + id_cmc_identityProof = true; + attr = attributes[i]; } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_idPOPLinkRandom)) { - SET vals = attributes[i].getValues(); - OCTET_STRING ostr = - (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(), - ASN1Util.encode(vals.elementAt(0)))); - randomSeed = ostr.toByteArray(); + id_cmc_idPOPLinkRandom = true; + vals = attributes[i].getValues(); } else { context.put(attributes[i].getType(), attributes[i]); } + } //for + + /** + * TODO: cfu: phase 2 should add enforcement for + * id_cmc_identityProofV2 and id_cmc_identification control + * when needed + */ + + /** + * now do the actual control processing + * (the postponed processing is so that we can capture + * the identification, if included) + */ + + UTF8String ident_s = null; + if (id_cmc_identification) { + ident_s = (UTF8String) (ASN1Util.decode(UTF8String.getTemplate(), + ASN1Util.encode(ident.elementAt(0)))); } - } + + // either V2 or not V2; can't be both + if (id_cmc_identityProofV2 && (attr != null)) { + boolean valid = verifyIdentityProofV2(attr, ident_s, + reqSeq); + if (!valid) { + SEQUENCE bpids = getRequestBpids(reqSeq); + context.put("identityProofV2", bpids); + return null; + } + } else if (id_cmc_identityProof && (attr != null)) { + boolean valid = verifyIdentityProof(attr, + reqSeq); + if (!valid) { + SEQUENCE bpids = getRequestBpids(reqSeq); + context.put("identityProof", bpids); + return null; + } + } + + if (id_cmc_idPOPLinkRandom && vals != null) { + OCTET_STRING ostr = (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(), + ASN1Util.encode(vals.elementAt(0)))); + randomSeed = ostr.toByteArray(); + } + } // numcontrols > 0 } SEQUENCE otherMsgSeq = pkiData.getOtherMsgSequence(); @@ -580,39 +636,54 @@ public abstract class EnrollProfile extends BasicProfile } private boolean verifyDigest(byte[] sharedSecret, byte[] text, byte[] bv) { - byte[] key = null; + MessageDigest hashAlg; try { - MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); - key = SHA1Digest.digest(sharedSecret); + hashAlg = MessageDigest.getInstance("SHA1"); } catch (NoSuchAlgorithmException ex) { - CMS.debug("EnrollProfile: No such algorithm for this message digest."); + CMS.debug("EnrollProfile:verifyDigest: " + ex.toString()); return false; } + return verifyDigest(sharedSecret, text, bv, hashAlg, hashAlg); + } + + /** + * verifyDigest verifies digest using the + * specified hashAlg and macAlg + * + * @param sharedSecret shared secret in bytes + * @param text data to be verified in bytes + * @param bv witness in bytes + * @param hashAlg hashing algorithm + * @param macAlg message authentication algorithm + * cfu + */ + private boolean verifyDigest(byte[] sharedSecret, byte[] text, byte[] bv, + MessageDigest hashAlg, MessageDigest macAlg) { + String method = "EnrollProfile:verifyDigest: "; + byte[] key = null; + CMS.debug(method + "in verifyDigest: hashAlg=" + hashAlg.toString() + + "; macAlg=" + macAlg.toString()); + key = hashAlg.digest(sharedSecret); + byte[] finalDigest = null; - try { - MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); - HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key); - hmacDigest.update(text); - finalDigest = hmacDigest.digest(); - } catch (NoSuchAlgorithmException ex) { - CMS.debug("EnrollProfile: No such algorithm for this message digest."); - return false; - } + HMACDigest hmacDigest = new HMACDigest(macAlg, key); + hmacDigest.update(text); + finalDigest = hmacDigest.digest(); if (finalDigest.length != bv.length) { - CMS.debug("EnrollProfile: The length of two HMAC digest are not the same."); + CMS.debug(method + " The length of two HMAC digest are not the same."); return false; } for (int j = 0; j < bv.length; j++) { if (bv[j] != finalDigest[j]) { - CMS.debug("EnrollProfile: The content of two HMAC digest are not the same."); + CMS.debug(method + " The content of two HMAC digest are not the same."); return false; } } - CMS.debug("EnrollProfile: The content of two HMAC digest are the same."); + CMS.debug(method + " The content of two HMAC digest are the same."); return true; } @@ -633,6 +704,106 @@ public abstract class EnrollProfile extends BasicProfile return bpids; } + /** + * verifyIdentityProofV2 handles IdentityProofV2 as defined by RFC5272 + * + * @param attr controlSequence of the PKI request PKIData + * @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 + */ + private boolean verifyIdentityProofV2( + TaggedAttribute attr, + UTF8String ident, + SEQUENCE reqSeq) { + String method = "EnrollProfile:verifyIdentityProofV2: "; + CMS.debug(method + " begins"); + + String ident_string = null; + if (ident != null) { + ident_string = ident.toString(); + // cfu: REMOVE + CMS.debug(method + "received ident String: " + ident_string); + } + + SET vals = attr.getValues(); // getting the IdentityProofV2 structure + if (vals.size() < 1) { + return false; + } + + String name = null; + try { + String configName = "cmc.sharedSecret.class"; + CMS.debug(method + "getting :" + configName); + name = CMS.getConfigStore().getString(configName); + CMS.debug(method + "Shared Secret plugin class name retrieved:" + + name); + } catch (Exception e) { + CMS.debug(method + " Failed to retrieve shared secret plugin class name"); + return false; + } + + ISharedToken tokenClass = null; + try { + tokenClass = (ISharedToken) Class.forName(name).newInstance(); + CMS.debug(method + "Shared Secret plugin class retrieved"); + } catch (ClassNotFoundException e) { + CMS.debug(method + " Failed to find class name: " + name); + return false; + } catch (InstantiationException e) { + CMS.debug("EnrollProfile: Failed to instantiate class: " + name); + return false; + } catch (IllegalAccessException e) { + CMS.debug(method + " Illegal access: " + name); + return false; + } + + String token = null; + if (ident_string != null) + token = tokenClass.getSharedToken(ident_string); + else + token = tokenClass.getSharedToken(mCMCData); + + if (token == null) { + CMS.debug(method + " Failed to retrieve shared secret"); + return false; + } + // cfu REMOVE + CMS.debug(method + "Shared Secret returned by tokenClass:" + token); + try { + IdentityProofV2 idV2val = (IdentityProofV2) (ASN1Util.decode(IdentityProofV2.getTemplate(), + ASN1Util.encode(vals.elementAt(0)))); + /** + * TODO: cfu: + * phase2: getting configurable allowable hashing and mac algorithms + */ + + DigestAlgorithm hashAlgID = DigestAlgorithm.fromOID(idV2val.getHashAlgID().getOID()); + MessageDigest hashAlg = MessageDigest.getInstance(hashAlgID.toString()); + // TODO: check against CA allowed algs later + + HMACAlgorithm macAlgId = HMACAlgorithm.fromOID(idV2val.getMacAlgId().getOID()); + MessageDigest macAlg = MessageDigest + .getInstance(CryptoUtil.getHMACtoMessageDigestName(macAlgId.toString())); + // TODO: check against CA allowed algs later + + OCTET_STRING witness = idV2val.getWitness(); + + byte[] witness_bytes = witness.toByteArray(); + byte[] request_bytes = ASN1Util.encode(reqSeq); // PKIData reqSequence field + return verifyDigest( + (ident_string != null) ? (token + ident_string).getBytes() : token.getBytes(), + request_bytes, + witness_bytes, + hashAlg, macAlg); + } catch (Exception e) { + CMS.debug(method + " Failed with Exception: " + e.toString()); + return false; + } + + } // verifyIdentityProofV2 + private boolean verifyIdentityProof(TaggedAttribute attr, SEQUENCE reqSeq) { SET vals = attr.getValues(); if (vals.size() < 1) |
