/* |
|
* Copyright (c) 2003, 2019, 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; |
|
import javax.security.sasl.SaslException; |
|
import javax.security.sasl.Sasl; |
|
// For HMAC_MD5 |
|
import java.security.NoSuchAlgorithmException; |
|
import java.security.MessageDigest; |
|
import java.util.Arrays; |
|
import java.util.logging.Logger; |
|
/** |
|
* Base class for implementing CRAM-MD5 client and server mechanisms. |
|
* |
|
* @author Vincent Ryan |
|
* @author Rosanna Lee |
|
*/ |
|
abstract class CramMD5Base { |
|
protected boolean completed = false; |
|
protected boolean aborted = false; |
|
protected byte[] pw; |
|
protected CramMD5Base() { |
|
initLogger(); |
|
} |
|
/** |
|
* Retrieves this mechanism's name. |
|
* |
|
* @return The string "CRAM-MD5". |
|
*/ |
|
public String getMechanismName() { |
|
return "CRAM-MD5"; |
|
} |
|
/** |
|
* Determines whether this mechanism has completed. |
|
* CRAM-MD5 completes after processing one challenge from the server. |
|
* |
|
* @return true if has completed; false otherwise; |
|
*/ |
|
public boolean isComplete() { |
|
return completed; |
|
} |
|
/** |
|
* Unwraps the incoming buffer. CRAM-MD5 supports no security layer. |
|
* |
|
* @throws SaslException If attempt to use this method. |
|
*/ |
|
public byte[] unwrap(byte[] incoming, int offset, int len) |
|
throws SaslException { |
|
if (completed) { |
|
throw new IllegalStateException( |
|
"CRAM-MD5 supports neither integrity nor privacy"); |
|
} else { |
|
throw new IllegalStateException( |
|
"CRAM-MD5 authentication not completed"); |
|
} |
|
} |
|
/** |
|
* Wraps the outgoing buffer. CRAM-MD5 supports no security layer. |
|
* |
|
* @throws SaslException If attempt to use this method. |
|
*/ |
|
public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException { |
|
if (completed) { |
|
throw new IllegalStateException( |
|
"CRAM-MD5 supports neither integrity nor privacy"); |
|
} else { |
|
throw new IllegalStateException( |
|
"CRAM-MD5 authentication not completed"); |
|
} |
|
} |
|
/** |
|
* Retrieves the negotiated property. |
|
* This method can be called only after the authentication exchange has |
|
* completed (i.e., when {@codeisComplete()} returns true); otherwise, a |
|
* {@codeSaslException} is thrown. |
|
* |
|
* @return value of property; only QOP is applicable to CRAM-MD5. |
|
* @exception IllegalStateException if this authentication exchange has not completed |
|
*/ |
|
public Object getNegotiatedProperty(String propName) { |
|
if (completed) { |
|
if (propName.equals(Sasl.QOP)) { |
|
return "auth"; |
|
} else { |
|
return null; |
|
} |
|
} else { |
|
throw new IllegalStateException( |
|
"CRAM-MD5 authentication not completed"); |
|
} |
|
} |
|
public void dispose() throws SaslException { |
|
clearPassword(); |
|
} |
|
protected void clearPassword() { |
|
if (pw != null) { |
|
// zero out password |
|
for (int i = 0; i < pw.length; i++) { |
|
pw[i] = (byte)0; |
|
} |
|
pw = null; |
|
} |
|
} |
|
protected void finalize() { |
|
clearPassword(); |
|
} |
|
static private final int MD5_BLOCKSIZE = 64; |
|
/** |
|
* Hashes its input arguments according to HMAC-MD5 (RFC 2104) |
|
* and returns the resulting digest in its ASCII representation. |
|
* |
|
* HMAC-MD5 function is described as follows: |
|
* |
|
* MD5(key XOR opad, MD5(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 |
|
* text is the data to be protected |
|
*/ |
|
final static String HMAC_MD5(byte[] key, byte[] text) |
|
throws NoSuchAlgorithmException { |
|
MessageDigest md5 = MessageDigest.getInstance("MD5"); |
|
/* digest the key if longer than 64 bytes */ |
|
if (key.length > MD5_BLOCKSIZE) { |
|
key = md5.digest(key); |
|
} |
|
byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */ |
|
byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */ |
|
byte[] digest; |
|
int i; |
|
/* store key in pads */ |
|
for (i = 0; i < key.length; i++) { |
|
ipad[i] = key[i]; |
|
opad[i] = key[i]; |
|
} |
|
/* XOR key with pads */ |
|
for (i = 0; i < MD5_BLOCKSIZE; i++) { |
|
ipad[i] ^= 0x36; |
|
opad[i] ^= 0x5c; |
|
} |
|
/* inner MD5 */ |
|
md5.update(ipad); |
|
md5.update(text); |
|
digest = md5.digest(); |
|
/* outer MD5 */ |
|
md5.update(opad); |
|
md5.update(digest); |
|
digest = md5.digest(); |
|
// Get character representation of digest |
|
StringBuffer digestString = new StringBuffer(); |
|
for (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)); |
|
} |
|
} |
|
Arrays.fill(ipad, (byte)0); |
|
Arrays.fill(opad, (byte)0); |
|
ipad = null; |
|
opad = null; |
|
return (digestString.toString()); |
|
} |
|
/** |
|
* Sets logger field. |
|
*/ |
|
private static synchronized void initLogger() { |
|
if (logger == null) { |
|
logger = Logger.getLogger(SASL_LOGGER_NAME); |
|
} |
|
} |
|
/** |
|
* Logger for debug messages |
|
*/ |
|
private static final String SASL_LOGGER_NAME = "javax.security.sasl"; |
|
protected static Logger logger; // set in initLogger(); lazily loads logger |
|
} |