/* * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.security.sasl.digest; import java.util.Map;import java.util.Arrays;import java.util.List;import java.util.Set;import java.util.logging.Logger;import java.util.logging.Level;import java.math.BigInteger;import java.util.Random;import java.security.Provider; import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.UnsupportedEncodingException;import java.io.IOException; import java.security.MessageDigest;import java.security.AccessController;import java.security.PrivilegedAction;import java.security.NoSuchAlgorithmException;import java.security.InvalidKeyException;import java.security.spec.KeySpec;import java.security.spec.InvalidKeySpecException;import java.security.InvalidAlgorithmParameterException; import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypto.Mac;import javax.crypto.SecretKeyFactory;import javax.crypto.BadPaddingException;import javax.crypto.NoSuchPaddingException;import javax.crypto.IllegalBlockSizeException;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import javax.crypto.spec.DESKeySpec;import javax.crypto.spec.DESedeKeySpec; import javax.security.sasl.*;import com.sun.security.sasl.util.AbstractSaslImpl; import javax.security.auth.callback.CallbackHandler; /** * Utility class for DIGEST-MD5 mechanism. Provides utility methods * and contains two inner classes which implement the SecurityCtx * interface. The inner classes provide the funtionality to allow * for quality-of-protection (QOP) with integrity checking and * privacy. * * @author Jonathan Bruce * @author Rosanna Lee */abstract class DigestMD5Base extends AbstractSaslImpl { /* ------------------------- Constants ------------------------ */ // Used for logging private static final String DI_CLASS_NAME = DigestIntegrity.class.getName(); private static final String DP_CLASS_NAME = DigestPrivacy.class.getName(); /* Constants - defined in RFC2831 */ protected static final int MAX_CHALLENGE_LENGTH = 2048; protected static final int MAX_RESPONSE_LENGTH = 4096; protected static final int DEFAULT_MAXBUF = 65536; /* Supported ciphers for 'auth-conf' */ protected static final int DES3 = 0; protected static final int RC4 = 1; protected static final int DES = 2; protected static final int RC4_56 = 3; protected static final int RC4_40 = 4; protected static final String[] CIPHER_TOKENS = { "3des", "rc4", "des", "rc4-56", "rc4-40" }; private static final String[] JCE_CIPHER_NAME = { "DESede/CBC/NoPadding", "RC4", "DES/CBC/NoPadding", }; /* * If QOP is set to 'auth-conf', a DIGEST-MD5 mechanism must have * support for the DES and Triple DES cipher algorithms (optionally, * support for RC4 [128/56/40 bit keys] ciphers) to provide for * confidentiality. See RFC 2831 for details. This implementation * provides support for DES, Triple DES and RC4 ciphers. * * The value of strength effects the strength of cipher used. The mappings * of 'high', 'medium', and 'low' give the following behaviour. * * HIGH_STRENGTH - Triple DES * - RC4 (128bit) * MEDIUM_STRENGTH - DES * - RC4 (56bit) * LOW_SRENGTH - RC4 (40bit) */ protected static final byte DES_3_STRENGTH = HIGH_STRENGTH; protected static final byte RC4_STRENGTH = HIGH_STRENGTH; protected static final byte DES_STRENGTH = MEDIUM_STRENGTH; protected static final byte RC4_56_STRENGTH = MEDIUM_STRENGTH; protected static final byte RC4_40_STRENGTH = LOW_STRENGTH; protected static final byte UNSET = (byte)0; protected static final byte[] CIPHER_MASKS = { DES_3_STRENGTH, RC4_STRENGTH, DES_STRENGTH, RC4_56_STRENGTH, RC4_40_STRENGTH }; private static final String SECURITY_LAYER_MARKER = ":00000000000000000000000000000000"; protected static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; /* ------------------- Variable Fields ----------------------- */ /* Used to track progress of authentication; step numbers from RFC 2831 */ protected int step; /* Used to get username/password, choose realm for client */ /* Used to obtain authorization, pw info, canonicalized authzid for server */ protected CallbackHandler cbh; protected SecurityCtx secCtx; protected byte[] H_A1; // component of response-value protected byte[] nonce; // server generated nonce /* Variables set when parsing directives in digest challenge/response. */ protected String negotiatedStrength; protected String negotiatedCipher; protected String negotiatedQop; protected String negotiatedRealm; protected boolean useUTF8 = false; protected String encoding = "8859_1"; // default unless server specifies utf-8 protected String digestUri; protected String authzid; // authzid or canonicalized authzid /** * Constucts an instance of DigestMD5Base. Calls super constructor * to parse properties for mechanism. * * @param props A map of property/value pairs * @param className name of class to use for logging * @param firstStep number of first step in authentication state machine * @param digestUri digestUri used in authentication * @param cbh callback handler used to get info required for auth * * @throws SaslException If invalid value found in props. */ protected DigestMD5Base(Map props, String className, int firstStep, String digestUri, CallbackHandler cbh) throws SaslException { super(props, className); // sets QOP, STENGTH and BUFFER_SIZE step = firstStep; this.digestUri = digestUri; this.cbh = cbh; } /** * Retrieves the SASL mechanism IANA name. * * @return The String "DIGEST-MD5" */ public String getMechanismName() { return "DIGEST-MD5"; } /** * Unwrap the incoming message using the wrap method of the secCtx object * instance. * * @param incoming The byte array containing the incoming bytes. * @param start The offset from which to read the byte array. * @param len The number of bytes to read from the offset. * @return The unwrapped message according to either the integrity or * privacy quality-of-protection specifications. * @throws SaslException if an error occurs when unwrapping the incoming * message */ public byte[] unwrap(byte[] incoming, int start, int len) throws SaslException { if (!completed) { throw new IllegalStateException( "DIGEST-MD5 authentication not completed"); } if (secCtx == null) { throw new IllegalStateException( "Neither integrity nor privacy was negotiated"); } return (secCtx.unwrap(incoming, start, len)); } /** * Wrap outgoing bytes using the wrap method of the secCtx object * instance. * * @param outgoing The byte array containing the outgoing bytes. * @param start The offset from which to read the byte array. * @param len The number of bytes to read from the offset. * @return The wrapped message according to either the integrity or * privacy quality-of-protection specifications. * @throws SaslException if an error occurs when wrapping the outgoing * message */ public byte[] wrap(byte[] outgoing, int start, int len) throws SaslException { if (!completed) { throw new IllegalStateException( "DIGEST-MD5 authentication not completed"); } if (secCtx == null) { throw new IllegalStateException( "Neither integrity nor privacy was negotiated"); } return (secCtx.wrap(outgoing, start, len)); } public void dispose() throws SaslException { if (secCtx != null) { secCtx = null; } } public Object getNegotiatedProperty(String propName) { if (completed) { if (propName.equals(Sasl.STRENGTH)) { return negotiatedStrength; } else { return super.getNegotiatedProperty(propName); } } else { throw new IllegalStateException( "DIGEST-MD5 authentication not completed"); } } /* ----------------- Digest-MD5 utilities ---------------- */ /** * Generate random-string used for digest-response. * This method uses Random to get random bytes and then * base64 encodes the bytes. Could also use binaryToHex() but this * is slightly faster and a more compact representation of the same info. * @return A non-null byte array containing the nonce value for the * digest challenge or response. * Could use SecureRandom to be more secure but it is very slow. */ /** This array maps the characters to their 6 bit values */ private final static char pem_array[] = { // 0 1 2 3 4 5 6 7 'A','B','C','D','E','F','G','H', // 0 'I','J','K','L','M','N','O','P', // 1 'Q','R','S','T','U','V','W','X', // 2 'Y','Z','a','b','c','d','e','f', // 3 'g','h','i','j','k','l','m','n', // 4 'o','p','q','r','s','t','u','v', // 5 'w','x','y','z','0','1','2','3', // 6 '4','5','6','7','8','9','+','/' // 7 }; // Make sure that this is a multiple of 3 private static final int RAW_NONCE_SIZE = 30; // Base 64 encoding turns each 3 bytes into 4 private static final int ENCODED_NONCE_SIZE = RAW_NONCE_SIZE*4/3; protected static final byte[] generateNonce() { // SecureRandom random = new SecureRandom(); Random random = new Random(); byte[] randomData = new byte[RAW_NONCE_SIZE]; random.nextBytes(randomData); byte[] nonce = new byte[ENCODED_NONCE_SIZE]; // Base64-encode bytes byte a, b, c; int j = 0; for (int i = 0; i < randomData.length; i += 3) { a = randomData[i]; b = randomData[i+1]; c = randomData[i+2]; nonce[j++] = (byte)(pem_array[(a >>> 2) & 0x3F]); nonce[j++] = (byte)(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); nonce[j++] = (byte)(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); nonce[j++] = (byte)(pem_array[c & 0x3F]); } return nonce; // %%% For testing using RFC 2831 example, uncomment the following 2 lines // System.out.println("!!!Using RFC 2831's cnonce for testing!!!"); // return "OA6MHXh6VqTrRk".getBytes(); } /** * Checks if a byte[] contains characters that must be quoted * and write the resulting, possibly escaped, characters to out. */ protected static void writeQuotedStringValue(ByteArrayOutputStream out, byte[] buf) { int len = buf.length; byte ch; for (int i = 0; i < len; i++) { ch = buf[i]; if (needEscape((char)ch)) { out.write('\\'); } out.write(ch); } } // See Section 7.2 of RFC 2831; double-quote character is not allowed // unless escaped; also escape the escape character and CTL chars except LWS private static boolean needEscape(String str) { int len = str.length(); for (int i = 0; i < len; i++) { if (needEscape(str.charAt(i))) { return true; } } return false; } // Determines whether a character needs to be escaped in a quoted string private static boolean needEscape(char ch) { return ch == '"' || // escape char ch == '\\' || // quote ch == 127 || // DEL // 0 <= ch <= 31 except CR, HT and LF (ch >= 0 && ch <= 31 && ch != 13 && ch != 9 && ch != 10); } protected static String quotedStringValue(String str) { if (needEscape(str)) { int len = str.length(); char[] buf = new char[len+len]; int j = 0; char ch; for (int i = 0; i < len; i++) { ch = str.charAt(i); if (needEscape(ch)) { buf[j++] = '\\'; } buf[j++] = ch; } return new String(buf, 0, j); } else { return str; } } /** * Convert a byte array to hexadecimal string. * * @param a non-null byte array * @return a non-null String contain the HEX value */ protected byte[] binaryToHex(byte[] digest) throws UnsupportedEncodingException { StringBuffer digestString = new StringBuffer(); for (int i = 0; i < digest.length; i ++) { if ((digest[i] & 0x000000ff) < 0x10) { digestString.append("0"+ Integer.toHexString(digest[i] & 0x000000ff)); } else { digestString.append( Integer.toHexString(digest[i] & 0x000000ff)); } } return digestString.toString().getBytes(encoding); } /** * Used to convert username-value, passwd or realm to 8859_1 encoding * if all chars in string are within the 8859_1 (Latin 1) encoding range. * * @param a non-null String * @return a non-nuill byte array containing the correct character encoding * for username, paswd or realm. */ protected byte[] stringToByte_8859_1(String str) throws SaslException { char[] buffer = str.toCharArray(); try { if (useUTF8) { for( int i = 0; i< buffer.length; i++ ) { if( buffer[i] > '\u00FF' ) { return str.getBytes("UTF8"); } } } return str.getBytes("8859_1"); } catch (UnsupportedEncodingException e) { throw new SaslException( "cannot encode string in UTF8 or 8859-1 (Latin-1)", e); } } protected static byte[] getPlatformCiphers() { byte[] ciphers = new byte[CIPHER_TOKENS.length]; for (int i = 0; i < JCE_CIPHER_NAME.length; i++) { try { // Checking whether the transformation is available from the // current installed providers. Cipher.getInstance(JCE_CIPHER_NAME[i]); logger.log(Level.FINE, "DIGEST01:Platform supports {0}", JCE_CIPHER_NAME[i]); ciphers[i] |= CIPHER_MASKS[i]; } catch (NoSuchAlgorithmException e) { // no implementation found for requested algorithm. } catch (NoSuchPaddingException e) { // no implementation found for requested algorithm. } } if (ciphers[RC4] != UNSET) { ciphers[RC4_56] |= CIPHER_MASKS[RC4_56]; ciphers[RC4_40] |= CIPHER_MASKS[RC4_40]; } return ciphers; } /** * Assembles response-value for digest-response. * * @param authMethod "AUTHENTICATE" for client-generated response; * "" for server-generated response * @return A non-null byte array containing the repsonse-value. * @throws NoSuchAlgorithmException if the platform does not have MD5 * digest support. * @throws UnsupportedEncodingException if a an error occurs * encoding a string into either Latin-1 or UTF-8. * @throws IOException if an error occurs writing to the output * byte array buffer. */ protected byte[] generateResponseValue( String authMethod, String digestUriValue, String qopValue, String usernameValue, String realmValue, char[] passwdValue, byte[] nonceValue, byte[] cNonceValue, int nonceCount, byte[] authzidValue ) throws NoSuchAlgorithmException, UnsupportedEncodingException, IOException { MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] hexA1, hexA2; ByteArrayOutputStream A2, beginA1, A1, KD; // A2 // -- // A2 = { "AUTHENTICATE:", digest-uri-value, // [:00000000000000000000000000000000] } // if auth-int or auth-conf // A2 = new ByteArrayOutputStream(); A2.write((authMethod + ":" + digestUriValue).getBytes(encoding)); if (qopValue.equals("auth-conf") ||Lines 1–500 of 1632. Load more
/* * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.security.sasl.digest; import java.util.Map; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.logging.Logger; import java.util.logging.Level; import java.math.BigInteger; import java.util.Random; import java.security.Provider; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.io.IOException; import java.security.MessageDigest; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import java.security.spec.KeySpec; import java.security.spec.InvalidKeySpecException; import java.security.InvalidAlgorithmParameterException; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.Mac; import javax.crypto.SecretKeyFactory; import javax.crypto.BadPaddingException; import javax.crypto.NoSuchPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.DESedeKeySpec; import javax.security.sasl.*; import com.sun.security.sasl.util.AbstractSaslImpl; import javax.security.auth.callback.CallbackHandler; /** * Utility class for DIGEST-MD5 mechanism. Provides utility methods * and contains two inner classes which implement the SecurityCtx * interface. The inner classes provide the funtionality to allow * for quality-of-protection (QOP) with integrity checking and * privacy. * * @author Jonathan Bruce * @author Rosanna Lee */ abstract class DigestMD5Base extends AbstractSaslImpl { /* ------------------------- Constants ------------------------ */ // Used for logging private static final String DI_CLASS_NAME = DigestIntegrity.class.getName(); private static final String DP_CLASS_NAME = DigestPrivacy.class.getName(); /* Constants - defined in RFC2831 */ protected static final int MAX_CHALLENGE_LENGTH = 2048; protected static final int MAX_RESPONSE_LENGTH = 4096; protected static final int DEFAULT_MAXBUF = 65536; /* Supported ciphers for 'auth-conf' */ protected static final int DES3 = 0; protected static final int RC4 = 1; protected static final int DES = 2; protected static final int RC4_56 = 3; protected static final int RC4_40 = 4; protected static final String[] CIPHER_TOKENS = { "3des", "rc4", "des", "rc4-56", "rc4-40" }; private static final String[] JCE_CIPHER_NAME = { "DESede/CBC/NoPadding", "RC4", "DES/CBC/NoPadding", }; /* * If QOP is set to 'auth-conf', a DIGEST-MD5 mechanism must have * support for the DES and Triple DES cipher algorithms (optionally, * support for RC4 [128/56/40 bit keys] ciphers) to provide for * confidentiality. See RFC 2831 for details. This implementation * provides support for DES, Triple DES and RC4 ciphers. * * The value of strength effects the strength of cipher used. The mappings * of 'high', 'medium', and 'low' give the following behaviour. * * HIGH_STRENGTH - Triple DES * - RC4 (128bit) * MEDIUM_STRENGTH - DES * - RC4 (56bit) * LOW_SRENGTH - RC4 (40bit) */ protected static final byte DES_3_STRENGTH = HIGH_STRENGTH; protected static final byte RC4_STRENGTH = HIGH_STRENGTH; protected static final byte DES_STRENGTH = MEDIUM_STRENGTH; protected static final byte RC4_56_STRENGTH = MEDIUM_STRENGTH; protected static final byte RC4_40_STRENGTH = LOW_STRENGTH; protected static final byte UNSET = (byte)0; protected static final byte[] CIPHER_MASKS = { DES_3_STRENGTH, RC4_STRENGTH, DES_STRENGTH, RC4_56_STRENGTH, RC4_40_STRENGTH }; private static final String SECURITY_LAYER_MARKER = ":00000000000000000000000000000000"; protected static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; /* ------------------- Variable Fields ----------------------- */ /* Used to track progress of authentication; step numbers from RFC 2831 */ protected int step; /* Used to get username/password, choose realm for client */ /* Used to obtain authorization, pw info, canonicalized authzid for server */ protected CallbackHandler cbh; protected SecurityCtx secCtx; protected byte[] H_A1; // component of response-value protected byte[] nonce; // server generated nonce /* Variables set when parsing directives in digest challenge/response. */ protected String negotiatedStrength; protected String negotiatedCipher; protected String negotiatedQop; protected String negotiatedRealm; protected boolean useUTF8 = false; protected String encoding = "8859_1"; // default unless server specifies utf-8 protected String digestUri; protected String authzid; // authzid or canonicalized authzid /** * Constucts an instance of DigestMD5Base. Calls super constructor * to parse properties for mechanism. * * @param props A map of property/value pairs * @param className name of class to use for logging * @param firstStep number of first step in authentication state machine * @param digestUri digestUri used in authentication * @param cbh callback handler used to get info required for auth * * @throws SaslException If invalid value found in props. */ protected DigestMD5Base(Map props, String className, int firstStep, String digestUri, CallbackHandler cbh) throws SaslException { super(props, className); // sets QOP, STENGTH and BUFFER_SIZE step = firstStep; this.digestUri = digestUri; this.cbh = cbh; } /** * Retrieves the SASL mechanism IANA name. * * @return The String "DIGEST-MD5" */ public String getMechanismName() { return "DIGEST-MD5"; } /** * Unwrap the incoming message using the wrap method of the secCtx object * instance. * * @param incoming The byte array containing the incoming bytes. * @param start The offset from which to read the byte array. * @param len The number of bytes to read from the offset. * @return The unwrapped message according to either the integrity or * privacy quality-of-protection specifications. * @throws SaslException if an error occurs when unwrapping the incoming * message */ public byte[] unwrap(byte[] incoming, int start, int len) throws SaslException { if (!completed) { throw new IllegalStateException( "DIGEST-MD5 authentication not completed"); } if (secCtx == null) { throw new IllegalStateException( "Neither integrity nor privacy was negotiated"); } return (secCtx.unwrap(incoming, start, len)); } /** * Wrap outgoing bytes using the wrap method of the secCtx object * instance. * * @param outgoing The byte array containing the outgoing bytes. * @param start The offset from which to read the byte array. * @param len The number of bytes to read from the offset. * @return The wrapped message according to either the integrity or * privacy quality-of-protection specifications. * @throws SaslException if an error occurs when wrapping the outgoing * message */ public byte[] wrap(byte[] outgoing, int start, int len) throws SaslException { if (!completed) { throw new IllegalStateException( "DIGEST-MD5 authentication not completed"); } if (secCtx == null) { throw new IllegalStateException( "Neither integrity nor privacy was negotiated"); } return (secCtx.wrap(outgoing, start, len)); } public void dispose() throws SaslException { if (secCtx != null) { secCtx = null; } } public Object getNegotiatedProperty(String propName) { if (completed) { if (propName.equals(Sasl.STRENGTH)) { return negotiatedStrength; } else { return super.getNegotiatedProperty(propName); } } else { throw new IllegalStateException( "DIGEST-MD5 authentication not completed"); } } /* ----------------- Digest-MD5 utilities ---------------- */ /** * Generate random-string used for digest-response. * This method uses Random to get random bytes and then * base64 encodes the bytes. Could also use binaryToHex() but this * is slightly faster and a more compact representation of the same info. * @return A non-null byte array containing the nonce value for the * digest challenge or response. * Could use SecureRandom to be more secure but it is very slow. */ /** This array maps the characters to their 6 bit values */ private final static char pem_array[] = { // 0 1 2 3 4 5 6 7 'A','B','C','D','E','F','G','H', // 0 'I','J','K','L','M','N','O','P', // 1 'Q','R','S','T','U','V','W','X', // 2 'Y','Z','a','b','c','d','e','f', // 3 'g','h','i','j','k','l','m','n', // 4 'o','p','q','r','s','t','u','v', // 5 'w','x','y','z','0','1','2','3', // 6 '4','5','6','7','8','9','+','/' // 7 }; // Make sure that this is a multiple of 3 private static final int RAW_NONCE_SIZE = 30; // Base 64 encoding turns each 3 bytes into 4 private static final int ENCODED_NONCE_SIZE = RAW_NONCE_SIZE*4/3; protected static final byte[] generateNonce() { // SecureRandom random = new SecureRandom(); Random random = new Random(); byte[] randomData = new byte[RAW_NONCE_SIZE]; random.nextBytes(randomData); byte[] nonce = new byte[ENCODED_NONCE_SIZE]; // Base64-encode bytes byte a, b, c; int j = 0; for (int i = 0; i < randomData.length; i += 3) { a = randomData[i]; b = randomData[i+1]; c = randomData[i+2]; nonce[j++] = (byte)(pem_array[(a >>> 2) & 0x3F]); nonce[j++] = (byte)(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); nonce[j++] = (byte)(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); nonce[j++] = (byte)(pem_array[c & 0x3F]); } return nonce; // %%% For testing using RFC 2831 example, uncomment the following 2 lines // System.out.println("!!!Using RFC 2831's cnonce for testing!!!"); // return "OA6MHXh6VqTrRk".getBytes(); } /** * Checks if a byte[] contains characters that must be quoted * and write the resulting, possibly escaped, characters to out. */ protected static void writeQuotedStringValue(ByteArrayOutputStream out, byte[] buf) { int len = buf.length; byte ch; for (int i = 0; i < len; i++) { ch = buf[i]; if (needEscape((char)ch)) { out.write('\\'); } out.write(ch); } } // See Section 7.2 of RFC 2831; double-quote character is not allowed // unless escaped; also escape the escape character and CTL chars except LWS private static boolean needEscape(String str) { int len = str.length(); for (int i = 0; i < len; i++) { if (needEscape(str.charAt(i))) { return true; } } return false; } // Determines whether a character needs to be escaped in a quoted string private static boolean needEscape(char ch) { return ch == '"' || // escape char ch == '\\' || // quote ch == 127 || // DEL // 0 <= ch <= 31 except CR, HT and LF (ch >= 0 && ch <= 31 && ch != 13 && ch != 9 && ch != 10); } protected static String quotedStringValue(String str) { if (needEscape(str)) { int len = str.length(); char[] buf = new char[len+len]; int j = 0; char ch; for (int i = 0; i < len; i++) { ch = str.charAt(i); if (needEscape(ch)) { buf[j++] = '\\'; } buf[j++] = ch; } return new String(buf, 0, j); } else { return str; } } /** * Convert a byte array to hexadecimal string. * * @param a non-null byte array * @return a non-null String contain the HEX value */ protected byte[] binaryToHex(byte[] digest) throws UnsupportedEncodingException { StringBuffer digestString = new StringBuffer(); for (int i = 0; i < digest.length; i ++) { if ((digest[i] & 0x000000ff) < 0x10) { digestString.append("0"+ Integer.toHexString(digest[i] & 0x000000ff)); } else { digestString.append( Integer.toHexString(digest[i] & 0x000000ff)); } } return digestString.toString().getBytes(encoding); } /** * Used to convert username-value, passwd or realm to 8859_1 encoding * if all chars in string are within the 8859_1 (Latin 1) encoding range. * * @param a non-null String * @return a non-nuill byte array containing the correct character encoding * for username, paswd or realm. */ protected byte[] stringToByte_8859_1(String str) throws SaslException { char[] buffer = str.toCharArray(); try { if (useUTF8) { for( int i = 0; i< buffer.length; i++ ) { if( buffer[i] > '\u00FF' ) { return str.getBytes("UTF8"); } } } return str.getBytes("8859_1"); } catch (UnsupportedEncodingException e) { throw new SaslException( "cannot encode string in UTF8 or 8859-1 (Latin-1)", e); } } protected static byte[] getPlatformCiphers() { byte[] ciphers = new byte[CIPHER_TOKENS.length]; for (int i = 0; i < JCE_CIPHER_NAME.length; i++) { try { // Checking whether the transformation is available from the // current installed providers. Cipher.getInstance(JCE_CIPHER_NAME[i]); logger.log(Level.FINE, "DIGEST01:Platform supports {0}", JCE_CIPHER_NAME[i]); ciphers[i] |= CIPHER_MASKS[i]; } catch (NoSuchAlgorithmException e) { // no implementation found for requested algorithm. } catch (NoSuchPaddingException e) { // no implementation found for requested algorithm. } } if (ciphers[RC4] != UNSET) { ciphers[RC4_56] |= CIPHER_MASKS[RC4_56]; ciphers[RC4_40] |= CIPHER_MASKS[RC4_40]; } return ciphers; } /** * Assembles response-value for digest-response. * * @param authMethod "AUTHENTICATE" for client-generated response; * "" for server-generated response * @return A non-null byte array containing the repsonse-value. * @throws NoSuchAlgorithmException if the platform does not have MD5 * digest support. * @throws UnsupportedEncodingException if a an error occurs * encoding a string into either Latin-1 or UTF-8. * @throws IOException if an error occurs writing to the output * byte array buffer. */ protected byte[] generateResponseValue( String authMethod, String digestUriValue, String qopValue, String usernameValue, String realmValue, char[] passwdValue, byte[] nonceValue, byte[] cNonceValue, int nonceCount, byte[] authzidValue ) throws NoSuchAlgorithmException, UnsupportedEncodingException, IOException { MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] hexA1, hexA2; ByteArrayOutputStream A2, beginA1, A1, KD; // A2 // -- // A2 = { "AUTHENTICATE:", digest-uri-value, // [:00000000000000000000000000000000] } // if auth-int or auth-conf // A2 = new ByteArrayOutputStream(); A2.write((authMethod + ":" + digestUriValue).getBytes(encoding)); if (qopValue.equals("auth-conf") ||