DigestMD5Base.java revision 1241
1241N/A * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved. 0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 0N/A * This code is free software; you can redistribute it and/or modify it 0N/A * under the terms of the GNU General Public License version 2 only, as 0N/A * published by the Free Software Foundation. Sun designates this 0N/A * particular file as subject to the "Classpath" exception as provided 0N/A * by Sun in the LICENSE file that accompanied this code. 0N/A * This code is distributed in the hope that it will be useful, but WITHOUT 0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 0N/A * version 2 for more details (a copy is included in the LICENSE file that 0N/A * accompanied this code). 0N/A * You should have received a copy of the GNU General Public License version 0N/A * 2 along with this work; if not, write to the Free Software Foundation, 0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 0N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 0N/A * CA 95054 USA or visit www.sun.com if you need additional information or 0N/A * have any questions. 0N/A * Utility class for DIGEST-MD5 mechanism. Provides utility methods 0N/A * and contains two inner classes which implement the SecurityCtx 0N/A * interface. The inner classes provide the funtionality to allow 0N/A * for quality-of-protection (QOP) with integrity checking and 0N/A * @author Jonathan Bruce 0N/A * @author Rosanna Lee 0N/A /* ------------------------- Constants ------------------------ */ 0N/A /* Constants - defined in RFC2831 */ 0N/A /* Supported ciphers for 'auth-conf' */ 0N/A protected static final int DES3 =
0;
0N/A protected static final int RC4 =
1;
0N/A protected static final int DES =
2;
0N/A * If QOP is set to 'auth-conf', a DIGEST-MD5 mechanism must have 0N/A * support for the DES and Triple DES cipher algorithms (optionally, 0N/A * support for RC4 [128/56/40 bit keys] ciphers) to provide for 0N/A * confidentiality. See RFC 2831 for details. This implementation 0N/A * provides support for DES, Triple DES and RC4 ciphers. 0N/A * The value of strength effects the strength of cipher used. The mappings 0N/A * of 'high', 'medium', and 'low' give the following behaviour. 0N/A * HIGH_STRENGTH - Triple DES 0N/A * MEDIUM_STRENGTH - DES 0N/A * LOW_SRENGTH - RC4 (40bit) 0N/A protected static final byte UNSET = (
byte)
0;
0N/A ":00000000000000000000000000000000";
0N/A /* ------------------- Variable Fields ----------------------- */ 0N/A /* Used to track progress of authentication; step numbers from RFC 2831 */ 0N/A /* Used to obtain authorization, pw info, canonicalized authzid for server */ 0N/A protected byte[]
H_A1;
// component of response-value 0N/A protected byte[]
nonce;
// server generated nonce 0N/A * Constucts an instance of DigestMD5Base. Calls super constructor 0N/A * to parse properties for mechanism. 0N/A * @param className name of class to use for logging 0N/A * @param firstStep number of first step in authentication state machine 0N/A * @param digestUri digestUri used in authentication 0N/A * @param cbh callback handler used to get info required for auth 0N/A * @throws SaslException If invalid value found in props. 0N/A * Retrieves the SASL mechanism IANA name. 0N/A * @return The String "DIGEST-MD5" 0N/A return "DIGEST-MD5";
0N/A * Unwrap the incoming message using the wrap method of the secCtx object 0N/A * @param incoming The byte array containing the incoming bytes. 0N/A * @param start The offset from which to read the byte array. 0N/A * @param len The number of bytes to read from the offset. 0N/A * @return The unwrapped message according to either the integrity or 0N/A * privacy quality-of-protection specifications. 0N/A * @throws SaslException if an error occurs when unwrapping the incoming 0N/A "DIGEST-MD5 authentication not completed");
0N/A "Neither integrity nor privacy was negotiated");
0N/A * Wrap outgoing bytes using the wrap method of the secCtx object 0N/A * @param outgoing The byte array containing the outgoing bytes. 0N/A * @param start The offset from which to read the byte array. 0N/A * @param len The number of bytes to read from the offset. 0N/A * @return The wrapped message according to either the integrity or 0N/A * privacy quality-of-protection specifications. 0N/A * @throws SaslException if an error occurs when wrapping the outgoing 0N/A "DIGEST-MD5 authentication not completed");
0N/A "Neither integrity nor privacy was negotiated");
0N/A "DIGEST-MD5 authentication not completed");
0N/A /* ----------------- Digest-MD5 utilities ---------------- */ 0N/A * Generate random-string used for digest-response. 0N/A * This method uses Random to get random bytes and then 0N/A * base64 encodes the bytes. Could also use binaryToHex() but this 0N/A * is slightly faster and a more compact representation of the same info. 0N/A * @return A non-null byte array containing the nonce value for the 0N/A * digest challenge or response. 0N/A * Could use SecureRandom to be more secure but it is very slow. 0N/A /** This array maps the characters to their 6 bit values */ 0N/A 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
// 0 0N/A 'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
// 1 0N/A 'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
// 2 0N/A 'Y',
'Z',
'a',
'b',
'c',
'd',
'e',
'f',
// 3 0N/A 'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
// 4 0N/A 'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
// 5 0N/A 'w',
'x',
'y',
'z',
'0',
'1',
'2',
'3',
// 6 0N/A '4',
'5',
'6',
'7',
'8',
'9',
'+',
'/' // 7 0N/A // Make sure that this is a multiple of 3 0N/A // Base 64 encoding turns each 3 bytes into 4 0N/A // SecureRandom random = new SecureRandom(); 0N/A // Base64-encode bytes 0N/A // %%% For testing using RFC 2831 example, uncomment the following 2 lines 0N/A // System.out.println("!!!Using RFC 2831's cnonce for testing!!!"); 0N/A // return "OA6MHXh6VqTrRk".getBytes(); 0N/A * Checks if a byte[] contains characters that must be quoted 0N/A * and write the resulting, possibly escaped, characters to out. 0N/A for (
int i =
0; i <
len; i++) {
0N/A // See Section 7.2 of RFC 2831; double-quote character is not allowed 0N/A // unless escaped; also escape the escape character and CTL chars except LWS 0N/A for (
int i =
0; i <
len; i++) {
0N/A // Determines whether a character needs to be escaped in a quoted string 0N/A return ch ==
'"' ||
// escape char 0N/A // 0 <= ch <= 31 except CR, HT and LF 0N/A for (
int i =
0; i <
len; i++) {
0N/A * Convert a byte array to hexadecimal string. 0N/A * @param a non-null byte array 0N/A * @return a non-null String contain the HEX value 0N/A * Used to convert username-value, passwd or realm to 8859_1 encoding 0N/A * if all chars in string are within the 8859_1 (Latin 1) encoding range. 0N/A * @param a non-null String 0N/A * @return a non-nuill byte array containing the correct character encoding 0N/A * for username, paswd or realm. 0N/A "cannot encode string in UTF8 or 8859-1 (Latin-1)", e);
0N/A // Checking whether the transformation is available from the 0N/A // current installed providers. 0N/A // no implementation found for requested algorithm. 0N/A // no implementation found for requested algorithm. 0N/A * Assembles response-value for digest-response. 0N/A * @param authMethod "AUTHENTICATE" for client-generated response; 0N/A * "" for server-generated response 0N/A * @return A non-null byte array containing the repsonse-value. 0N/A * @throws NoSuchAlgorithmException if the platform does not have MD5 0N/A * @throws UnsupportedEncodingException if a an error occurs 0N/A * encoding a string into either Latin-1 or UTF-8. 0N/A * @throws IOException if an error occurs writing to the output 0N/A * byte array buffer. 0N/A // A2 = { "AUTHENTICATE:", digest-uri-value, 0N/A // [:00000000000000000000000000000000] } // if auth-int or auth-conf 0N/A // H(user-name : realm-value : passwd) 0N/A // if no realm, realm will be an empty string 0N/A // A1 = { H ( {user-name : realm-value : passwd } ), 0N/A // : nonce-value, : cnonce-value : authzid-value 0N/A * Takes 'nonceCount' value and returns HEX value of the value. 0N/A * @return A non-null String representing the current NONCE-COUNT 0N/A * Parses digest-challenge string, extracting each token 0N/A * @param buf A non-null digest-challenge string. 0N/A * @param multipleAllowed true if multiple qop or realm or QOP directives 0N/A * @throws SaslException if the buf cannot be parsed according to RFC 2831 0N/A // Empty element, skip separator and lws 0N/A // Check whether value is quoted 0N/A // LWS that occurs after key 0N/A // Getting a quoted value 0N/A // quoted-pair = "\" CHAR ==> CHAR 0N/A // Trailing escape in a quoted value 0N/A "Unmatched quote found for directive: " 0N/A ++i;
// Skip closing quote 0N/A "Expecting comma or linear whitespace after quoted string: \"" 0N/A // Is character a linear white space? 0N/A // LWS = [CRLF] 1*( SP | HT ) 0N/A // %%% Note that we're checking individual bytes instead of CRLF 0N/A case 13:
// US-ASCII CR, carriage return 0N/A case 10:
// US-ASCII LF, linefeed 0N/A case 32:
// US-ASCII SP, space 0N/A case 9:
// US-ASCII HT, horizontal-tab 0N/A // Skip all linear white spaces 0N/A * fill out the challengeVal array. 0N/A * @param key A non-null String challenge token name. 0N/A * @param value A non-null String token value. 0N/A * @throws SaslException if a either the key or the value is null 0N/A // > 1 realm specified 0N/A "DIGEST-MD5: peer sent more than one " +
0N/A break;
// end search 0N/A * Implementation of the SecurityCtx interface allowing for messages 0N/A * between the client and server to be integrity checked. After a 0N/A * successful DIGEST-MD5 authentication, integtrity checking is invoked 0N/A * if the SASL QOP (quality-of-protection) is set to 'auth-int'. 0N/A * Further details on the integrity-protection mechanism can be found 0N/A * at section 2.3 - Integrity protection in the 0N/A * @author Jonathan Bruce 0N/A /* Used for generating integrity keys - specified in RFC 2831*/ 0N/A "client-to-server signing key magic constant";
0N/A "server-to-client signing key magic constant";
0N/A /* Key pairs for integrity checking */ 0N/A protected byte[]
myKi;
// == Kic for client; == Kis for server 0N/A protected byte[]
peerKi;
// == Kis for client; == Kic for server 0N/A // outgoing messageType and sequenceNum 0N/A * Initializes DigestIntegrity implementation of SecurityCtx to 0N/A * enable DIGEST-MD5 integrity checking. 0N/A * @throws SaslException if an error is encountered generating the 0N/A * key-pairs for integrity checking. 0N/A /* Initialize magic strings */ 0N/A "DIGEST-MD5: Error encoding strings into UTF-8", e);
0N/A "required to create integrity key pairs", e);
0N/A "algorithm used to create integrity key pairs", e);
0N/A /* Message type is a fixed value */ 0N/A * Generate client-server, server-client key pairs for DIGEST-MD5 0N/A * integrity checking. 0N/A * @throws UnsupportedEncodingException if the UTF-8 encoding is not 0N/A * supported on the platform. 0N/A * @throws IOException if an error occurs when writing to or from the 0N/A * byte array output buffers. 0N/A * @throws NoSuchAlgorithmException if the MD5 message digest algorithm 0N/A // Both client-magic-keys and server-magic-keys are the same length 0N/A // Kic: Key for protecting msgs from client to server. 0N/A // Kis: Key for protecting msgs from server to client 0N/A // No need to recopy H_A1 0N/A * Append MAC onto outgoing message. 0N/A * @param outgoing A non-null byte array containing the outgoing message. 0N/A * @param start The offset from which to read the byte array. 0N/A * @param len The non-zero number of bytes for be read from the offset. 0N/A * @return The message including the integrity MAC 0N/A * @throws SaslException if an error is encountered converting a string 0N/A * into a UTF-8 byte encoding, or if the MD5 message digest algorithm 0N/A * cannot be found or if there is an error writing to the byte array 0N/A /* wrapped = message, MAC, message type, sequence number */ 0N/A /* Start with message itself */ 0N/A /* Add MAC[0..9] to message */ 0N/A /* Add message type [0..1] */ 0N/A /* Add sequence number [0..3] */ 0N/A * Return verified message without MAC - only if the received MAC 0N/A * and re-generated MAC are the same. 0N/A * @param incoming A non-null byte array containing the incoming 0N/A * @param start The offset from which to read the byte array. 0N/A * @param len The non-zero number of bytes to read from the offset 0N/A * @return The verified message or null if integrity checking fails. 0N/A * @throws SaslException if an error is encountered converting a string 0N/A * into a UTF-8 byte encoding, or if the MD5 message digest algorithm 0N/A * cannot be found or if there is an error writing to the byte array 0N/A // shave off last 16 bytes of message 0N/A /* Get Msg, MAC, msgType, sequenceNum */ 0N/A /* Calculate MAC to ensure integrity */ 0N/A /* First, compare MAC's before updating any of our state */ 0N/A // Discard message and do not increment sequence number 0N/A /* Ensure server-sequence numbers are correct */ 0N/A "sequencing of messages from server. Got: " +
0N/A // Increment sequence number and return message 0N/A * Generates MAC to be appended onto out-going messages. 0N/A * @param Ki A non-null byte array containing the key for the digest 0N/A * @param SeqNum A non-null byte array contain the sequence number 0N/A * @param msg The message to be digested 0N/A * @param start The offset from which to read the msg byte array 0N/A * @param len The non-zero number of bytes to be read from the offset 0N/A * @return The MAC of a message. 0N/A * @throws SaslException if an error occurs when generating MAC. 0N/A /* First 10 bytes of HMAC_MD5 digest */ 0N/A "key of HMAC-MD5 hash.", e);
0N/A "instance of MD5 digest algorithm", e);
0N/A * Increment own sequence number and set answer in NBO sequenceNum field. 0N/A * Implementation of the SecurityCtx interface allowing for messages 0N/A * between the client and server to be integrity checked and encrypted. 0N/A * After a successful DIGEST-MD5 authentication, privacy is invoked if the 0N/A * SASL QOP (quality-of-protection) is set to 'auth-conf'. 0N/A * Further details on the integrity-protection mechanism can be found 0N/A * at section 2.4 - Confidentiality protection in 0N/A * @author Jonathan Bruce 0N/A /* Used for generating privacy keys - specified in RFC 2831 */ 0N/A "Digest H(A1) to client-to-server sealing key magic constant";
0N/A "Digest H(A1) to server-to-client sealing key magic constant";
0N/A * Initializes the cipher object instances for encryption and decryption. 0N/A * @throws SaslException if an error occurs with the Key 0N/A * initialization, or a string cannot be encoded into a byte array 0N/A * using the UTF-8 encoding, or an error occurs when writing to a 0N/A * byte array output buffers or the mechanism cannot load the MD5 0N/A * message digest algorithm or invalid initialization parameters are 0N/A * passed to the cipher object instances. 0N/A super(
clientMode);
// generate Kic, Kis keys for integrity-checking. 0N/A "DIGEST-MD5: Error encoding string value into UTF-8", e);
0N/A "buffers required to generate cipher keys", e);
0N/A "instance of required cipher or digest", e);
0N/A * Generates client-server and server-client keys to encrypt and 0N/A * decrypt messages. Also generates IVs for DES ciphers. 0N/A * @throws IOException if an error occurs when writing to or from the 0N/A * byte array output buffers. 0N/A * @throws NoSuchAlgorithmException if the MD5 message digest algorithm 0N/A * @throws UnsupportedEncodingException if an UTF-8 encoding is not 0N/A * supported on the platform. 0N/A * @throw SaslException if an error occurs initializing the keys and 0N/A * IVs for the chosen cipher. 0N/A /* Kcc = MD5{H(A1)[0..n], "Digest ... client-to-server"} */ 0N/A n =
5;
/* H(A1)[0..5] */ 0N/A n =
7;
/* H(A1)[0..7] */ 0N/A }
else {
// des and 3des and rc4 0N/A n =
16;
/* H(A1)[0..16] */ 0N/A /* {H(A1)[0..n], "Digest ... client-to-server..."} */ 0N/A // Both client-magic-keys and server-magic-keys are the same length 0N/A /* Kcc: Key for encrypting messages from client->server */ 0N/A /* Kcs: Key for decrypting messages from server->client */ 0N/A // No need to copy H_A1 again since it hasn't changed 0N/A /* Initialize cipher objects */ 0N/A // Use "NoPadding" when specifying cipher names 0N/A // RFC 2831 already defines padding rules for producing 0N/A // 8-byte aligned blocks 0N/A // Set up the DES IV, which is the last 8 bytes of Kcc/Kcs 0N/A // Initialize cipher objects 0N/A "specification used.", e);
0N/A "algorithem parameter used to create cipher instance", e);
0N/A "padding used for chosen cipher", e);
0N/A "used to initialize keys", e);
0N/A // ------------------------------------------------------------------- 0N/A * Encrypt out-going message. 0N/A * @param outgoing A non-null byte array containing the outgoing message. 0N/A * @param start The offset from which to read the byte array. 0N/A * @param len The non-zero number of bytes to be read from the offset. 0N/A * @return The encrypted message. 0N/A * @throws SaslException if an error occurs when writing to or from the 0N/A * byte array output buffers or if the MD5 message digest algorithm 0N/A * cannot loaded or if an UTF-8 encoding is not supported on the 0N/A /* HMAC(Ki, {SeqNum, msg})[0..9] */ 0N/A // Calculate padding 0N/A /* {msg, pad, HMAC(Ki, {SeqNum, msg}[0..9])} */ 0N/A /* CIPHER(Kc, {msg, pad, HMAC(Ki, {SeqNum, msg}[0..9])}) */ 0N/A // Do CBC (chaining) across packets 0N/A // update() can return null 0N/A "DIGEST-MD5: Invalid block size for cipher", e);
0N/A * Decrypt incoming messages and verify their integrity. 0N/A * @param incoming A non-null byte array containing the incoming 0N/A * encrypted message. 0N/A * @param start The offset from which to read the byte array. 0N/A * @param len The non-zero number of bytes to read from the offset 0N/A * @return The decrypted, verified message or null if integrity 0N/A * @throws SaslException if there are the SASL buffer is empty or if 0N/A * if an error occurs reading the SASL buffer. 0N/A /* Get cipherMsg; msgType; sequenceNum */ 0N/A "DIGEST33:Expecting sequence num: {0}",
0N/A /* CIPHER(Kc, {msg, pad, HMAC(Ki, {SeqNum, msg}[0..9])}) */ 0N/A // Do CBC (chaining) across packets 0N/A // update() can return null 0N/A "sizes used with chosen cipher", e);
0N/A // get value of last octet of the byte array 0N/A // Discard message and do not increment sequence number 0N/A "DIGEST39:Incorrect padding: {0}",
0N/A /* Re-calculate MAC to ensure integrity */ 0N/A // First, compare MACs before updating state 0N/A // Discard message and do not increment sequence number 0N/A /* Ensure sequence number is correct */ 0N/A "sequencing of messages from server. Got: " +
0N/A /* Check message type */ 0N/A // Increment sequence number and return message 0N/A // Get a copy of the message without padding 0N/A // ---------------- DES and 3 DES key manipulation routines 0N/A * Sets the parity bit (0th bit) in each byte so that each byte 0N/A * contains an odd number of 1's. 0N/A * Expands a 7-byte array into an 8-byte array that contains parity bits 0N/A * The binary format of a cryptographic key is: 0N/A * (B1,B2,...,B7,P1,B8,...B14,P2,B15,...,B49,P7,B50,...,B56,P8) 0N/A * where (B1,B2,...,B56) are the independent bits of a DES key and 0N/A * (PI,P2,...,P8) are reserved for parity bits computed on the preceding 0N/A * seven independent bits and set so that the parity of the octet is odd, 0N/A * i.e., there is an odd number of "1" bits in the octet. 0N/A "Invalid length of DES Key Value:" +
len);
0N/A // Shift 7 bits each time into a byte 0N/A * Create parity-adjusted keys suitable for DES / DESede encryption. 0N/A * @param input A non-null byte array containing key material for 0N/A * @param desStrength A string specifying eithe a DES or a DESede key. 0N/A * @return SecretKey An instance of either DESKeySpec or DESedeKeySpec. 0N/A * @throws NoSuchAlgorithmException if the either the DES or DESede 0N/A * algorithms cannote be lodaed by JCE. 0N/A * @throws InvalidKeyException if an invalid array of bytes is used 0N/A * as a key for DES or DESede. 0N/A * @throws InvalidKeySpecException in an invalid parameter is passed 0N/A * to either te DESKeySpec of the DESedeKeySpec constructors. 0N/A // Generate first subkey using first 7 bytes 0N/A // Generate second subkey using second 7 bytes 0N/A // Construct 24-byte encryption-decryption-encryption sequence 0N/A "DIGEST47:3DES key ede: ",
ede);
0N/A "DIGEST48:3DES key material: ",