mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-24 02:12:15 -05:00
Initial implementation of CRAM-MD5 support for IMAP and SMTP.
Patch contributed by Russ Weeks <rweeks@gmail.com> in <605ac1c0-808a-4f67-8c4d-736eec9587f8@e27g2000yqd.googlegroups.com>
This commit is contained in:
parent
921b022fb5
commit
5a85446779
@ -62,6 +62,16 @@
|
|||||||
android:id="@+id/account_security_type"
|
android:id="@+id/account_security_type"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="fill_parent" />
|
android:layout_width="fill_parent" />
|
||||||
|
<TextView
|
||||||
|
android:text="@string/account_setup_incoming_authtype_label"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/account_auth_type"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent" />
|
||||||
<TextView
|
<TextView
|
||||||
android:text="@string/account_setup_incoming_port_label"
|
android:text="@string/account_setup_incoming_port_label"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -298,6 +298,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
|||||||
<string name="account_setup_incoming_webdav_server_label">WebDAV (Exchange) server</string>
|
<string name="account_setup_incoming_webdav_server_label">WebDAV (Exchange) server</string>
|
||||||
<string name="account_setup_incoming_port_label">Port</string>
|
<string name="account_setup_incoming_port_label">Port</string>
|
||||||
<string name="account_setup_incoming_security_label">Security type</string>
|
<string name="account_setup_incoming_security_label">Security type</string>
|
||||||
|
<string name="account_setup_incoming_authtype_label">Authentication type</string>
|
||||||
<string name="account_setup_incoming_security_none_label">None</string>
|
<string name="account_setup_incoming_security_none_label">None</string>
|
||||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL (if available)</string>
|
<string name="account_setup_incoming_security_ssl_optional_label">SSL (if available)</string>
|
||||||
<string name="account_setup_incoming_security_ssl_label">SSL (always)</string>
|
<string name="account_setup_incoming_security_ssl_label">SSL (always)</string>
|
||||||
|
@ -59,6 +59,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
private EditText mServerView;
|
private EditText mServerView;
|
||||||
private EditText mPortView;
|
private EditText mPortView;
|
||||||
private Spinner mSecurityTypeView;
|
private Spinner mSecurityTypeView;
|
||||||
|
private Spinner mAuthTypeView;
|
||||||
private EditText mImapPathPrefixView;
|
private EditText mImapPathPrefixView;
|
||||||
private Button mImapFolderDrafts;
|
private Button mImapFolderDrafts;
|
||||||
private Button mImapFolderSent;
|
private Button mImapFolderSent;
|
||||||
@ -99,6 +100,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
mServerView = (EditText)findViewById(R.id.account_server);
|
mServerView = (EditText)findViewById(R.id.account_server);
|
||||||
mPortView = (EditText)findViewById(R.id.account_port);
|
mPortView = (EditText)findViewById(R.id.account_port);
|
||||||
mSecurityTypeView = (Spinner)findViewById(R.id.account_security_type);
|
mSecurityTypeView = (Spinner)findViewById(R.id.account_security_type);
|
||||||
|
mAuthTypeView = (Spinner)findViewById(R.id.account_auth_type);
|
||||||
mImapPathPrefixView = (EditText)findViewById(R.id.imap_path_prefix);
|
mImapPathPrefixView = (EditText)findViewById(R.id.imap_path_prefix);
|
||||||
mImapFolderDrafts = (Button)findViewById(R.id.account_imap_folder_drafts);
|
mImapFolderDrafts = (Button)findViewById(R.id.account_imap_folder_drafts);
|
||||||
mImapFolderSent = (Button)findViewById(R.id.account_imap_folder_sent);
|
mImapFolderSent = (Button)findViewById(R.id.account_imap_folder_sent);
|
||||||
@ -126,11 +128,22 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
new SpinnerOption(4, getString(R.string.account_setup_incoming_security_tls_label)),
|
new SpinnerOption(4, getString(R.string.account_setup_incoming_security_tls_label)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SpinnerOption authTypes[] =
|
||||||
|
{
|
||||||
|
new SpinnerOption(0, "PLAIN" ),
|
||||||
|
new SpinnerOption(1, "CRAM_MD5" )
|
||||||
|
};
|
||||||
|
|
||||||
ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
|
ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
|
||||||
android.R.layout.simple_spinner_item, securityTypes);
|
android.R.layout.simple_spinner_item, securityTypes);
|
||||||
securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
mSecurityTypeView.setAdapter(securityTypesAdapter);
|
mSecurityTypeView.setAdapter(securityTypesAdapter);
|
||||||
|
|
||||||
|
ArrayAdapter<SpinnerOption> authTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
|
||||||
|
android.R.layout.simple_spinner_item, authTypes);
|
||||||
|
authTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
mAuthTypeView.setAdapter(authTypesAdapter);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Updates the port when the user changes the security type. This allows
|
* Updates the port when the user changes the security type. This allows
|
||||||
* us to show a reasonable default which the user can change.
|
* us to show a reasonable default which the user can change.
|
||||||
@ -228,6 +241,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
findViewById(R.id.imap_folder_setup_section).setVisibility(View.GONE);
|
findViewById(R.id.imap_folder_setup_section).setVisibility(View.GONE);
|
||||||
findViewById(R.id.webdav_path_prefix_section).setVisibility(View.GONE);
|
findViewById(R.id.webdav_path_prefix_section).setVisibility(View.GONE);
|
||||||
findViewById(R.id.webdav_path_debug_section).setVisibility(View.GONE);
|
findViewById(R.id.webdav_path_debug_section).setVisibility(View.GONE);
|
||||||
|
findViewById(R.id.account_auth_type).setVisibility(View.GONE);
|
||||||
mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER);
|
mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER);
|
||||||
|
|
||||||
|
|
||||||
@ -261,6 +275,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
/** Hide the unnecessary fields */
|
/** Hide the unnecessary fields */
|
||||||
findViewById(R.id.imap_path_prefix_section).setVisibility(View.GONE);
|
findViewById(R.id.imap_path_prefix_section).setVisibility(View.GONE);
|
||||||
findViewById(R.id.imap_folder_setup_section).setVisibility(View.GONE);
|
findViewById(R.id.imap_folder_setup_section).setVisibility(View.GONE);
|
||||||
|
findViewById(R.id.account_auth_type).setVisibility(View.GONE);
|
||||||
if (uri.getPath() != null && uri.getPath().length() > 0)
|
if (uri.getPath() != null && uri.getPath().length() > 0)
|
||||||
{
|
{
|
||||||
String[] pathParts = uri.getPath().split("\\|");
|
String[] pathParts = uri.getPath().split("\\|");
|
||||||
@ -433,9 +448,19 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
path = path + "|" + mWebdavMailboxPathView.getText();
|
path = path + "|" + mWebdavMailboxPathView.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String userInfo;
|
||||||
|
if (mAccountSchemes[securityType].startsWith("imap"))
|
||||||
|
{
|
||||||
|
String authType = ((SpinnerOption)mAuthTypeView.getSelectedItem()).label;
|
||||||
|
userInfo = authType + ":" + mUsernameView.getText() + ":" + mPasswordView.getText();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
userInfo = mUsernameView.getText() + ":" + mPasswordView.getText();
|
||||||
|
}
|
||||||
URI uri = new URI(
|
URI uri = new URI(
|
||||||
mAccountSchemes[securityType],
|
mAccountSchemes[securityType],
|
||||||
mUsernameView.getText() + ":" + mPasswordView.getText(),
|
userInfo,
|
||||||
mServerView.getText().toString(),
|
mServerView.getText().toString(),
|
||||||
Integer.parseInt(mPortView.getText().toString()),
|
Integer.parseInt(mPortView.getText().toString()),
|
||||||
path, // path
|
path, // path
|
||||||
|
@ -16,12 +16,19 @@ import com.beetstra.jutf7.CharsetProvider;
|
|||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -51,6 +58,8 @@ public class ImapStore extends Store
|
|||||||
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
||||||
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
||||||
|
|
||||||
|
private enum AuthType { PLAIN, CRAM_MD5 };
|
||||||
|
|
||||||
private static final int IDLE_READ_TIMEOUT = 29 * 60 * 1000; // 29 minutes
|
private static final int IDLE_READ_TIMEOUT = 29 * 60 * 1000; // 29 minutes
|
||||||
private static final int IDLE_REFRESH_INTERVAL = 20 * 60 * 1000; // 20 minutes
|
private static final int IDLE_REFRESH_INTERVAL = 20 * 60 * 1000; // 20 minutes
|
||||||
|
|
||||||
@ -66,6 +75,7 @@ public class ImapStore extends Store
|
|||||||
private String mUsername;
|
private String mUsername;
|
||||||
private String mPassword;
|
private String mPassword;
|
||||||
private int mConnectionSecurity;
|
private int mConnectionSecurity;
|
||||||
|
private AuthType mAuthType;
|
||||||
private volatile String mPathPrefix;
|
private volatile String mPathPrefix;
|
||||||
private volatile String mCombinedPrefix = null;
|
private volatile String mCombinedPrefix = null;
|
||||||
private volatile String mPathDelimeter;
|
private volatile String mPathDelimeter;
|
||||||
@ -87,11 +97,11 @@ public class ImapStore extends Store
|
|||||||
private HashMap<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();
|
private HashMap<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* imap://user:password@server:port CONNECTION_SECURITY_NONE
|
* imap://auth:user:password@server:port CONNECTION_SECURITY_NONE
|
||||||
* imap+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
* imap+tls://auth:user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||||
* imap+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
* imap+tls+://auth:user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
||||||
* imap+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
* imap+ssl+://auth:user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
||||||
* imap+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
* imap+ssl://auth:user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
||||||
*
|
*
|
||||||
* @param _uri
|
* @param _uri
|
||||||
*/
|
*/
|
||||||
@ -147,12 +157,10 @@ public class ImapStore extends Store
|
|||||||
|
|
||||||
if (uri.getUserInfo() != null)
|
if (uri.getUserInfo() != null)
|
||||||
{
|
{
|
||||||
String[] userInfoParts = uri.getUserInfo().split(":", 2);
|
String[] userInfoParts = uri.getUserInfo().split(":");
|
||||||
mUsername = userInfoParts[0];
|
mAuthType = AuthType.valueOf(userInfoParts[0]);
|
||||||
if (userInfoParts.length > 1)
|
mUsername = userInfoParts[1];
|
||||||
{
|
mPassword = userInfoParts[2];
|
||||||
mPassword = userInfoParts[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((uri.getPath() != null) && (uri.getPath().length() > 0))
|
if ((uri.getPath() != null) && (uri.getPath().length() > 0))
|
||||||
@ -1899,9 +1907,14 @@ public class ImapStore extends Store
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// TODO eventually we need to add additional authentication
|
if ( mAuthType == AuthType.CRAM_MD5 )
|
||||||
// options such as SASL
|
{
|
||||||
executeSimpleCommand("LOGIN \"" + escapeString(mUsername) + "\" \"" + escapeString(mPassword) + "\"", true);
|
authCramMD5();
|
||||||
|
}
|
||||||
|
else if ( mAuthType == AuthType.PLAIN )
|
||||||
|
{
|
||||||
|
executeSimpleCommand("LOGIN \"" + escapeString(mUsername) + "\" \"" + escapeString(mPassword) + "\"", true);
|
||||||
|
}
|
||||||
authSuccess = true;
|
authSuccess = true;
|
||||||
}
|
}
|
||||||
catch (ImapException ie)
|
catch (ImapException ie)
|
||||||
@ -2002,6 +2015,88 @@ public class ImapStore extends Store
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void authCramMD5() throws AuthenticationFailedException, MessagingException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String tag = sendCommand("AUTHENTICATE CRAM-MD5", false);
|
||||||
|
byte[] buf = new byte[ 1024 ];
|
||||||
|
int b64NonceLen = 0;
|
||||||
|
for ( int i = 0; i < buf.length; i++ )
|
||||||
|
{
|
||||||
|
buf[ i ] = (byte)mIn.read();
|
||||||
|
if ( buf[i] == 0x0a )
|
||||||
|
{
|
||||||
|
b64NonceLen = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( b64NonceLen == 0 )
|
||||||
|
{
|
||||||
|
throw new AuthenticationFailedException( "Error negotiating CRAM-MD5: nonce too long." );
|
||||||
|
}
|
||||||
|
byte[] b64NonceTrim = new byte[ b64NonceLen - 2 ];
|
||||||
|
System.arraycopy(buf, 1, b64NonceTrim, 0, b64NonceLen - 2);
|
||||||
|
byte[] nonce = Base64.decodeBase64(b64NonceTrim);
|
||||||
|
if ( K9.DEBUG )
|
||||||
|
{
|
||||||
|
Log.d(K9.LOG_TAG, "Got nonce: " + new String( b64NonceTrim, "US-ASCII" ) );
|
||||||
|
Log.d(K9.LOG_TAG, "Plaintext nonce: " + new String( nonce, "US-ASCII" ) );
|
||||||
|
}
|
||||||
|
byte[] ipad = new byte[64];
|
||||||
|
byte[] opad = new byte[64];
|
||||||
|
byte[] secretBytes = mPassword.getBytes("US-ASCII");
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
if ( secretBytes.length > 64 )
|
||||||
|
{
|
||||||
|
secretBytes = md.digest(secretBytes);
|
||||||
|
}
|
||||||
|
System.arraycopy(secretBytes, 0, ipad, 0, secretBytes.length);
|
||||||
|
System.arraycopy(secretBytes, 0, opad, 0, secretBytes.length);
|
||||||
|
for ( int i = 0; i < ipad.length; i++ ) ipad[i] ^= 0x36;
|
||||||
|
for ( int i = 0; i < opad.length; i++ ) opad[i] ^= 0x5c;
|
||||||
|
md.update(ipad);
|
||||||
|
byte[] firstPass = md.digest(nonce);
|
||||||
|
md.update(opad);
|
||||||
|
byte[] result = md.digest(firstPass);
|
||||||
|
String plainCRAM = mUsername + " " + new String(Hex.encodeHex(result));
|
||||||
|
byte[] b64CRAM = Base64.encodeBase64(plainCRAM.getBytes("US-ASCII"));
|
||||||
|
if ( K9.DEBUG )
|
||||||
|
{
|
||||||
|
Log.d(K9.LOG_TAG, "Username == " + mUsername);
|
||||||
|
Log.d( K9.LOG_TAG, "plainCRAM: " + plainCRAM );
|
||||||
|
Log.d( K9.LOG_TAG, "b64CRAM: " + new String(b64CRAM, "US-ASCII"));
|
||||||
|
}
|
||||||
|
mOut.write( b64CRAM );
|
||||||
|
mOut.write( new byte[] { 0x0d, 0x0a } );
|
||||||
|
mOut.flush();
|
||||||
|
int respLen = 0;
|
||||||
|
for ( int i = 0; i < buf.length; i++ )
|
||||||
|
{
|
||||||
|
buf[ i ] = (byte)mIn.read();
|
||||||
|
if ( buf[i] == 0x0a )
|
||||||
|
{
|
||||||
|
respLen = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String toMatch = tag + " OK";
|
||||||
|
String respStr = new String( buf, 0, respLen );
|
||||||
|
if ( !respStr.startsWith( toMatch ) )
|
||||||
|
{
|
||||||
|
throw new AuthenticationFailedException( "CRAM-MD5 error: " + respStr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( IOException ioe )
|
||||||
|
{
|
||||||
|
throw new AuthenticationFailedException( "CRAM-MD5 Auth Failed." );
|
||||||
|
}
|
||||||
|
catch ( NoSuchAlgorithmException nsae )
|
||||||
|
{
|
||||||
|
throw new AuthenticationFailedException( "MD5 Not Available." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void setReadTimeout(int millis) throws SocketException
|
protected void setReadTimeout(int millis) throws SocketException
|
||||||
{
|
{
|
||||||
mSocket.setSoTimeout(millis);
|
mSocket.setSoTimeout(millis);
|
||||||
|
@ -18,7 +18,10 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -222,6 +225,7 @@ public class SmtpTransport extends Transport
|
|||||||
*/
|
*/
|
||||||
boolean authLoginSupported = false;
|
boolean authLoginSupported = false;
|
||||||
boolean authPlainSupported = false;
|
boolean authPlainSupported = false;
|
||||||
|
boolean authCramMD5Supported = false;
|
||||||
for (String result : results)
|
for (String result : results)
|
||||||
{
|
{
|
||||||
if (result.matches(".*AUTH.*LOGIN.*$") == true)
|
if (result.matches(".*AUTH.*LOGIN.*$") == true)
|
||||||
@ -232,12 +236,20 @@ public class SmtpTransport extends Transport
|
|||||||
{
|
{
|
||||||
authPlainSupported = true;
|
authPlainSupported = true;
|
||||||
}
|
}
|
||||||
|
if (result.matches(".*AUTH.*CRAM-MD5.*$") == true)
|
||||||
|
{
|
||||||
|
authCramMD5Supported = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUsername != null && mUsername.length() > 0 && mPassword != null
|
if (mUsername != null && mUsername.length() > 0 && mPassword != null
|
||||||
&& mPassword.length() > 0)
|
&& mPassword.length() > 0)
|
||||||
{
|
{
|
||||||
if (authPlainSupported)
|
if (authCramMD5Supported)
|
||||||
|
{
|
||||||
|
saslAuthCramMD5(mUsername, mPassword);
|
||||||
|
}
|
||||||
|
else if (authPlainSupported)
|
||||||
{
|
{
|
||||||
saslAuthPlain(mUsername, mPassword);
|
saslAuthPlain(mUsername, mPassword);
|
||||||
}
|
}
|
||||||
@ -484,4 +496,48 @@ public class SmtpTransport extends Transport
|
|||||||
throw me;
|
throw me;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saslAuthCramMD5(String username, String password) throws MessagingException,
|
||||||
|
AuthenticationFailedException, IOException
|
||||||
|
{
|
||||||
|
List<String> respList = executeSimpleCommand( "AUTH CRAM-MD5" );
|
||||||
|
if ( respList.size() != 1 ) throw new AuthenticationFailedException( "Unable to negotiate CRAM-MD5" );
|
||||||
|
String b64Nonce = respList.get(0);
|
||||||
|
byte[] nonce = Base64.decodeBase64( b64Nonce.getBytes("US-ASCII") );
|
||||||
|
byte[] ipad = new byte[64];
|
||||||
|
byte[] opad = new byte[64];
|
||||||
|
byte[] secretBytes = password.getBytes("US-ASCII");
|
||||||
|
MessageDigest md;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
md = MessageDigest.getInstance("MD5");
|
||||||
|
}
|
||||||
|
catch ( NoSuchAlgorithmException nsae )
|
||||||
|
{
|
||||||
|
throw new AuthenticationFailedException( "MD5 Not Available." );
|
||||||
|
}
|
||||||
|
if ( secretBytes.length > 64 )
|
||||||
|
{
|
||||||
|
secretBytes = md.digest(secretBytes);
|
||||||
|
}
|
||||||
|
System.arraycopy(secretBytes, 0, ipad, 0, secretBytes.length);
|
||||||
|
System.arraycopy(secretBytes, 0, opad, 0, secretBytes.length);
|
||||||
|
for ( int i = 0; i < ipad.length; i++ ) ipad[i] ^= 0x36;
|
||||||
|
for ( int i = 0; i < opad.length; i++ ) opad[i] ^= 0x5c;
|
||||||
|
md.update(ipad);
|
||||||
|
byte[] firstPass = md.digest(nonce);
|
||||||
|
md.update(opad);
|
||||||
|
byte[] result = md.digest(firstPass);
|
||||||
|
String plainCRAM = username + " " + new String(Hex.encodeHex(result));
|
||||||
|
byte[] b64CRAM = Base64.encodeBase64(plainCRAM.getBytes("US-ASCII"));
|
||||||
|
String b64CRAMString = new String( b64CRAM, "US-ASCII" );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
executeSimpleCommand( b64CRAMString );
|
||||||
|
}
|
||||||
|
catch ( MessagingException me )
|
||||||
|
{
|
||||||
|
throw new AuthenticationFailedException( "Unable to negotiate MD5 CRAM" );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user