diff --git a/base/common/src/org/dogtagpki/tps/main/Util.java b/base/common/src/org/dogtagpki/tps/main/Util.java index a2551f90904..0ffd1e1b635 100644 --- a/base/common/src/org/dogtagpki/tps/main/Util.java +++ b/base/common/src/org/dogtagpki/tps/main/Util.java @@ -344,6 +344,15 @@ public static TPSBuffer computeMACdes3des(PK11SymKey symKey, TPSBuffer input, TP } //Use AES-CMAC (SCP03, counter method) to calculate cryptogram, constant determines whether it is a card or host cryptogram + //Stub for temporarily commented out routine. + + public static TPSBuffer compute_AES_CMAC_Cryptogram(SymmetricKey symKey, TPSBuffer context, byte kdfConstant) + throws EBaseException { + + throw new EBaseException("Not Implemented"); + } + + /* public static TPSBuffer compute_AES_CMAC_Cryptogram(SymmetricKey symKey, TPSBuffer context, byte kdfConstant) throws EBaseException { @@ -402,10 +411,20 @@ public static TPSBuffer compute_AES_CMAC_Cryptogram(SymmetricKey symKey, TPSBuff return output.substr(0,8); } + */ // Implements agorithm http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38b.pdf // Input an aes key of 128, 192, or 256 bits + // + + //Stub for temporarily commented out routine. + + public static TPSBuffer computeAES_CMAC(SymmetricKey aesKey, TPSBuffer input) throws EBaseException { + throw new EBaseException("Not Implemented!"); + } + + /* public static TPSBuffer computeAES_CMAC(SymmetricKey aesKey, TPSBuffer input) throws EBaseException { String method = "Util.computeAES_CMAC:"; @@ -532,7 +551,7 @@ private static byte[] getAES_CMAC_SubKey(byte[] input) { } return output; } - + */ public static TPSBuffer computeMAC(PK11SymKey symKey, TPSBuffer input, TPSBuffer icv) throws EBaseException { TPSBuffer output = null; TPSBuffer result = null; diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java index 4b7205147e5..86e2687ba8c 100644 --- a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java +++ b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java @@ -33,10 +33,12 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.Key; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.StringTokenizer; +import javax.crypto.Mac; import org.mozilla.jss.CryptoManager; import org.mozilla.jss.asn1.ANY; @@ -111,7 +113,6 @@ import org.mozilla.jss.util.Password; import com.netscape.cmsutil.crypto.CryptoUtil; -import com.netscape.cmsutil.util.HMACDigest; /** * Tool for creating CMC full request @@ -1071,14 +1072,15 @@ private static int addIdentityProofV2Attr(int bpid, return -1; } - MessageDigest mac; + Mac hmac; 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!"); + hmac = Mac.getInstance(CryptoUtil.getHMACAlgName(macAlgString),"Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(key); + hmac.init(secKey); + hmac.update(b); + finalDigest = hmac.doFinal(); + } catch (Exception ex) { + System.out.println(method + "Can't calucualte hmac digest: " + ex); return -1; } @@ -1126,14 +1128,16 @@ private static int addIdentityProofAttr(int bpid, SEQUENCE seq, SEQUENCE reqSequ return -1; } + Mac hmac; try { - MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1"); - HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key); - hmacDigest.update(b); - finalDigest = hmacDigest.digest(); - } catch (NoSuchAlgorithmException ex) { + hmac = Mac.getInstance("HmacSHA1","Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(key); + hmac.init(secKey); + hmac.update(b); + finalDigest = hmac.doFinal(); + } catch (Exception ex) { System.out.println("CMCRequest::addIdentityProofAttr() - " - + "No such algorithm!"); + + "Can't calculate hmac Digest!"); return -1; } @@ -1547,16 +1551,16 @@ private static PopLinkWitnessV2 createPopLinkWitnessV2Attr( return null; } - MessageDigest mac; + Mac hmac; // (3) compute MAC over R from (1) using key from (2) try { - mac = MessageDigest.getInstance( - CryptoUtil.getHMACtoMessageDigestName(macAlgString)); - HMACDigest hmacDigest = new HMACDigest(mac, key); - hmacDigest.update(random_R); - finalDigest = hmacDigest.digest(); - } catch (NoSuchAlgorithmException ex) { - System.out.println(method + "No such algorithm!"); + hmac = Mac.getInstance(CryptoUtil.getHMACAlgName(macAlgString),"Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(key); + hmac.init(secKey); + hmac.update(random_R); + finalDigest = hmac.doFinal(); + } catch (Exception ex) { + System.out.println(method + "Can't calculate Hmac digest! " + ex); return null; } @@ -1887,10 +1891,11 @@ private static PKIData constructDecryptedPopRequest( 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(); + Mac hmac = Mac.getInstance("HmacSHA256","Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(challenge); + hmac.init(secKey); + hmac.update(ASN1Util.encode(request)); + popProofValue = hmac.doFinal(); System.out.println(method + "popProofValue length = " + popProofValue.length); } catch (Exception ex) { CryptoUtil.obscureBytes(challenge, "random"); diff --git a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java index e2923b39842..7b7c4ae1760 100644 --- a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java +++ b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java @@ -26,9 +26,11 @@ import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Paths; +import java.security.Key; import java.security.KeyPair; import java.security.MessageDigest; import java.security.PublicKey; +import javax.crypto.Mac; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -88,7 +90,6 @@ import com.netscape.certsrv.client.ClientConfig; import com.netscape.certsrv.client.PKIClient; import com.netscape.cmsutil.crypto.CryptoUtil; -import com.netscape.cmsutil.util.HMACDigest; /** * A command-line utility used to generate a Certificate Request Message @@ -756,11 +757,12 @@ public OCTET_STRING createIDPOPLinkWitness() throws Exception { 0x6a, 0x12, 0x6b, 0x3c, 0x4c, 0x3f, 0x00, 0x14, 0x51, 0x61, 0x15, 0x22, 0x23, 0x5f, 0x5e, 0x69 }; - - MessageDigest digest2 = MessageDigest.getInstance("SHA1"); - HMACDigest hmacDigest = new HMACDigest(digest2, key1); - hmacDigest.update(b); - byte[] finalDigest = hmacDigest.digest(); + + Mac hmac = Mac.getInstance("HmacSHA1","Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(key1); + hmac.init(secKey); + hmac.update(b); + byte[] finalDigest = hmac.doFinal(); return new OCTET_STRING(finalDigest); } diff --git a/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java b/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java index a7bf34cb960..d8fc223049c 100644 --- a/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java +++ b/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java @@ -279,9 +279,12 @@ public static void main(String args[]) throws Exception { 0x6a, 0x12, 0x6b, 0x3c, 0x4c, 0x3f, 0x00, 0x14, 0x51, 0x61, 0x15, 0x22, 0x23, 0x5f, 0x5e, 0x69 }; - HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key1); - hmacDigest.update(b); - finalDigest = hmacDigest.digest(); + + Mac hmac = Mac.getInstance("HmacSHA1","Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(key1); + hmac.init(secKey); + hmac.update(b); + finalDigest = hmac.doFinal(); OCTET_STRING ostr = new OCTET_STRING(finalDigest); Attribute attr = new Attribute(OBJECT_IDENTIFIER.id_cmc_idPOPLinkWitness, ostr); 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 26a3a294996..eb78bec2190 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 @@ -22,6 +22,7 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; +import java.security.Key; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -33,7 +34,7 @@ import java.util.Enumeration; import java.util.Locale; import java.util.StringTokenizer; - +import javax.crypto.Mac; import org.mozilla.jss.CryptoManager; import org.mozilla.jss.asn1.ASN1Util; import org.mozilla.jss.asn1.ASN1Value; @@ -129,7 +130,6 @@ import com.netscape.cmscore.apps.CMSEngine; import com.netscape.cmscore.security.JssSubsystem; import com.netscape.cmsutil.crypto.CryptoUtil; -import com.netscape.cmsutil.util.HMACDigest; /** * This class implements a generic enrollment profile. @@ -1257,9 +1257,15 @@ private BigInteger verifyDecryptedPOP(Locale locale, logger.warn(msg); return null; } - HMACDigest hmacDigest = new HMACDigest(digest, challenge_b); - hmacDigest.update(cmc_msg); - byte[] proofValue = hmacDigest.digest(); + + Mac hmac; + String hmacAlgName = CryptoUtil.getHMACAlgName(CryptoUtil.getDefaultHashAlgName() + "-HMAC"); + hmac = Mac.getInstance(hmacAlgName,"Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(challenge_b); + hmac.init(secKey); + hmac.update(cmc_msg); + byte[] proofValue = hmac.doFinal(); + if (proofValue == null) { msg = method + "proofValue null after hmacDigest.digest returned"; logger.warn(msg); @@ -1618,11 +1624,20 @@ private boolean verifyDigest(byte[] sharedSecret, byte[] text, byte[] bv, } key = hashAlg.digest(sharedSecret); + Mac hmac; byte[] finalDigest = null; - HMACDigest hmacDigest = new HMACDigest(macAlg, key); - hmacDigest.update(text); - finalDigest = hmacDigest.digest(); + try { + hmac = Mac.getInstance(CryptoUtil.getHMACAlgName(macAlg.getAlgorithm() + "-HMAC"),"Mozilla-JSS"); + Key secKey = CryptoUtil.importHmacSha1Key(key); + hmac.init(secKey); + hmac.update(text); + finalDigest = hmac.doFinal(); + } catch (Exception e) { + logger.debug(method + "hmac exception: " + e); + //Old code expected to get something for finalDigest, possibly null + finalDigest = null; + } if (finalDigest.length != bv.length) { logger.warn(method + " The length of two HMAC digest are not the same."); diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java b/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java index f9d52b4161f..f5e225fba92 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java @@ -142,6 +142,18 @@ public Map computeCardKeys(SymmetricKey masterKey, byte[] } //Compute the AES based CMAC operation. Used to derive session keys and cryptograms + // + + // Stub version: + + public byte[] kdf_AES_CMAC_SCP03(SymmetricKey masterKey, byte[] context, byte kdfConstant, + int kdfOutputSizeBytes) throws EBaseException { + + throw new EBaseException("Not Implemented!"); + } + + // Comment out for now until can be moved + /* public byte[] kdf_AES_CMAC_SCP03(SymmetricKey masterKey, byte[] context, byte kdfConstant, int kdfOutputSizeBytes) throws EBaseException { @@ -213,12 +225,22 @@ public byte[] kdf_AES_CMAC_SCP03(SymmetricKey masterKey, byte[] context, byte kd return output.toByteArray(); } + */ /******************************************************************************* Key Derivation Function in Counter Mode using PRF = SHA256HMAC (NIST SP 800-108) Calculates 384 bits of diversified output from the provided master key (K_I) *******************************************************************************/ + // Stub: + private byte[] kdf_CM_SHA256_HMAC_L384(SymmetricKey masterKey, byte[] context, byte kdfLabel, + int kdfOutputSizeBytes, CryptoToken token) throws EBaseException { + throw new EBaseException("Not Implemented."); + } + + // Comment out for now. + + /* private byte[] kdf_CM_SHA256_HMAC_L384(SymmetricKey masterKey, byte[] context, byte kdfLabel, int kdfOutputSizeBytes, CryptoToken token) throws EBaseException { @@ -279,7 +301,10 @@ private byte[] kdf_CM_SHA256_HMAC_L384(SymmetricKey masterKey, byte[] context, b return finalOutput; } + */ + + // This should be ok since it just uses the HMAC digest exposed by JSS from NSS. private byte[] sha256HMAC(SymmetricKey masterKey, // HMAC Secret Key (K_I) byte[] hmac_data_input, // HMAC Input (i||04||00||context||0180) int hMAC_DATA_INPUT_SIZE, // Input Length @@ -316,6 +341,15 @@ private byte[] sha256HMAC(SymmetricKey masterKey, // HMAC Secret Key (K_I) // For now calling code only using 128 // Will move later to common class used by both tks and tps + // Stub: + + public static byte[] computeAES_CMAC(SymmetricKey aesKey, byte[] input) throws EBaseException { + throw new EBaseException("Not Implemented."); + } + + //Comment out for now. + + /* public static byte[] computeAES_CMAC(SymmetricKey aesKey, byte[] input) throws EBaseException { String method = "NistSP800_108KDF.computeAES_CMAC:"; @@ -441,8 +475,18 @@ public static byte[] computeAES_CMAC(SymmetricKey aesKey, byte[] input) throws E return encData; } - + */ // SCP03 AES-CMAC support function + // + // Stub: + + private static byte[] getAES_CMAC_SubKey(byte[] input) { + return null; + } + + // Comment out for now. + + /* private static byte[] getAES_CMAC_SubKey(byte[] input) { byte[] output = new byte[input.length]; @@ -459,6 +503,7 @@ private static byte[] getAES_CMAC_SubKey(byte[] input) { } return output; } + */ // Collection of informal invocations of api used to create various session keys // Done with test data. diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java index c49808996fc..5a5255a3561 100644 --- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java +++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java @@ -42,6 +42,8 @@ import java.security.interfaces.DSAParams; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.RSAPublicKey; +import java.security.Key; +import javax.crypto.spec.IvParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -2748,6 +2750,47 @@ public static AlgorithmIdentifier getDefaultHashAlg() return hashAlg; } + /** + * importHmacSha1Key returns a key based on a byte array, + * which is originally a password. Used for the HMAC Digest algs. + * + * @param key the byte array representing the original password or secret. + * @return The JSS SymKey + * + */ + public static Key importHmacSha1Key(byte[] key) + throws Exception { + + final String WRAPPING_ALGORITHM = "AES/CBC/PKCS5Padding"; + + Key wrappingKey = null; + + final String keyGenAlgorithm = "AES"; + final int wrappingKeyLength = 256; + + logger.debug("CryptoUtil.importHmacSha1Key: entering"); + + javax.crypto.KeyGenerator keyGen = javax.crypto.KeyGenerator.getInstance(keyGenAlgorithm, "Mozilla-JSS"); + keyGen.init(wrappingKeyLength); + wrappingKey = keyGen.generateKey(); + + byte[] iv = new byte[16]; + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + + javax.crypto.Cipher wrappingCipher = javax.crypto.Cipher.getInstance(WRAPPING_ALGORITHM, "Mozilla-JSS"); + wrappingCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, wrappingKey, ivParameterSpec); + + byte[] wrappedKeyData = wrappingCipher.doFinal(key); + + javax.crypto.Cipher unwrappingCipher = javax.crypto.Cipher.getInstance(WRAPPING_ALGORITHM, "Mozilla-JSS"); + unwrappingCipher.init(javax.crypto.Cipher.UNWRAP_MODE, wrappingKey, ivParameterSpec); + + return unwrappingCipher.unwrap(wrappedKeyData, + SymmetricKey.SHA1_HMAC.toString(), + javax.crypto.Cipher.SECRET_KEY); + } + + // The following are useful mapping functions /** @@ -2840,6 +2883,26 @@ public static String getNameFromHashAlgorithm(AlgorithmIdentifier ai) throw new NoSuchAlgorithmException(); } + /** + * Maps from HMACAlgorithm name to JSS Provider HMAC Alg name. + */ + public static String getHMACAlgName(String name) { + logger.debug("CrytoUtil: getHMaCAlgName: name: " + name); + String mdName = "HmacSHA256"; + if (name != null) { + if (name.equals("SHA-256-HMAC")) { + mdName = "HmacSHA256"; + } else if (name.equals("SHA-384-HMAC")) { + mdName = "HmacSHAS384"; + } else if (name.equals("SHA-512-HMAC")) { + mdName = "HmacSHA512"; + } + } + + logger.debug("CrytoUtil: getHMaCAlgName: returning: " +mdName); + return mdName; + } + /* * Useful method to map KeyWrap algorithms to an OID. * This is not yet defined within JSS, although it will be valuable to do diff --git a/base/util/src/com/netscape/cmsutil/util/HMACDigest.java b/base/util/src/com/netscape/cmsutil/util/HMACDigest.java deleted file mode 100644 index c0d86184c19..00000000000 --- a/base/util/src/com/netscape/cmsutil/util/HMACDigest.java +++ /dev/null @@ -1,198 +0,0 @@ -// --- BEGIN COPYRIGHT BLOCK --- -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; version 2 of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// -// (C) 2007 Red Hat, Inc. -// All rights reserved. -// --- END COPYRIGHT BLOCK --- -package com.netscape.cmsutil.util; - -import java.security.MessageDigest; - -/** - * This class implements the HMAC algorithm specified in RFC 2104 using - * any MessageDigest. - * - * @author mikep - * @version $Revision$, $Date$ - * @see java.security.MessageDigest - */ -public class HMACDigest implements Cloneable { - public static final int PAD_BYTES = 64; - public static final int IPAD = 0x36; - public static final int OPAD = 0x5C; - - /** - * inner padding - key XORd with ipad - */ - private byte[] mKeyIpad = new byte[PAD_BYTES]; - - /** - * outer padding - key XORd with opad - */ - private byte[] mKeyOpad = new byte[PAD_BYTES]; - - /** - * The real MessageDigest - */ - private MessageDigest mMD = null; - - /** - * Creates an HMACDigest - * - * @param md The MessageDigest to be used for the HMAC calculation. It - * must be clonable. - */ - public HMACDigest(MessageDigest md) { - mMD = md; - } - - /** - * Creates an HMACDigest and initializes the HMAC function - * with the given key. - * - * @param md The MessageDigest to be used for the HMAC calculation. It - * must be clonable. - * @param key The key value to be used in the HMAC calculation - */ - public HMACDigest(MessageDigest md, byte[] key) { - this(md); - init(key); - } - - /** - * Return the MessageDigest used for this HMAC - */ - public MessageDigest getMessageDigest() { - return mMD; - } - - /** - * Initialize the HMAC function - * - * The HMAC transform looks like: - * - * hash(key XOR opad, hash(key XOR ipad, text)) - * - * where key is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected - * - * This routine must be called after every reset. - * - * @param key The password used to protect the hash value - */ - public void init(byte[] key) { - int i; - - reset(); - - // If the key is longer than 64 bytes, just hash it down - if (key.length > 64) { - key = mMD.digest(key); - mMD.reset(); // Redundant? - } - - // Copy the key. Truncate if key is too long - for (i = 0; i < key.length && i < PAD_BYTES; i++) { - mKeyIpad[i] = key[i]; - mKeyOpad[i] = key[i]; - } - - // XOR in the pads - for (i = 0; i < PAD_BYTES; i++) { - mKeyIpad[i] ^= IPAD; - mKeyOpad[i] ^= OPAD; - } - - mMD.update(mKeyIpad); - - // Hmmm, we really shouldn't key Opad around in memory for so - // long, but it would just force the user to key their key around - // until digest() time. Oh well, at least clear the key and Ipad - for (i = 0; i < PAD_BYTES; i++) { - mKeyIpad[i] = 0; - } - for (i = 0; i < key.length; i++) { - key[0] = 0; - } - } - - /** - * Updates the digest using the specified array of bytes. - * - * @param input the array of bytes. - */ - public void update(byte[] input) { - mMD.update(input); - } - - /** - * Completes the HMAC computation with the outer pad - * The digest is reset after this call is made. - * - * @return the array of bytes for the resulting hash value. - */ - public byte[] digest() { - byte[] finalDigest; - byte[] innerDigest = mMD.digest(); - - mMD.reset(); // Redundant? - mMD.update(mKeyOpad); - mMD.update(innerDigest); - finalDigest = mMD.digest(); - reset(); // Clear pad arrays - return finalDigest; - } - - /** - * Resets the digest for further use. - */ - public void reset() { - int i; - - mMD.reset(); - - // Clear out the pads - for (i = 0; i < PAD_BYTES; i++) { - mKeyIpad[i] = 0; - mKeyOpad[i] = 0; - } - } - - /** - * Clone the HMACDigest - * - * @return a clone if the implementation is cloneable. - * @exception CloneNotSupportedException if this is called on a - * MessageDigest implementation that does not support Cloneable. - */ - public Object clone() throws CloneNotSupportedException { - int i; - - HMACDigest hd = (HMACDigest) super.clone(); - - hd.mKeyOpad = new byte[PAD_BYTES]; - hd.mKeyIpad = new byte[PAD_BYTES]; - - for (i = 0; i < PAD_BYTES; i++) { - hd.mKeyOpad[i] = mKeyOpad[i]; - hd.mKeyIpad[i] = mKeyIpad[i]; - } - - hd.mMD = (MessageDigest) mMD.clone(); - return hd; - } - -}