diff options
Diffstat (limited to 'src/com/isode/stroke/sasl/DIGESTMD5ClientAuthenticator.java')
-rw-r--r-- | src/com/isode/stroke/sasl/DIGESTMD5ClientAuthenticator.java | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/com/isode/stroke/sasl/DIGESTMD5ClientAuthenticator.java b/src/com/isode/stroke/sasl/DIGESTMD5ClientAuthenticator.java new file mode 100644 index 0000000..639c353 --- /dev/null +++ b/src/com/isode/stroke/sasl/DIGESTMD5ClientAuthenticator.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2010-2013 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.sasl; + +import com.isode.stroke.sasl.ClientAuthenticator; +import com.isode.stroke.sasl.DIGESTMD5Properties; +import com.isode.stroke.crypto.CryptoProvider; +import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.base.ByteArray; +import com.isode.stroke.stringcodecs.Hexify; + +public class DIGESTMD5ClientAuthenticator extends ClientAuthenticator { + + private enum Step { + Initial, + Response, + Final + }; + private Step step; + private String host = ""; + private String cnonce = ""; + private CryptoProvider crypto; + private DIGESTMD5Properties challenge = new DIGESTMD5Properties(); + + public DIGESTMD5ClientAuthenticator(final String host, final String nonce, CryptoProvider crypto) { + super("DIGEST-MD5"); + this.step = Step.Initial; + this.host = host; + this.cnonce = nonce; + this.crypto = crypto; + } + + public SafeByteArray getResponse() { + if (Step.Initial.equals(step)) { + return null; + } + else if (Step.Response.equals(step)) { + String realm = ""; + if (challenge.getValue("realm") != null) { + realm = challenge.getValue("realm"); + } + String qop = "auth"; + String digestURI = "xmpp/" + host; + String nc = "00000001"; + + ByteArray A11 = crypto.getMD5Hash(new SafeByteArray().append(getAuthenticationID()).append(":").append(realm).append(":").append(getPassword())); + ByteArray A12 = new ByteArray().append(":").append(challenge.getValue("nonce")).append(":").append(cnonce); + // Compute the response value + ByteArray A1 = A11.append(A12); + if (!getAuthorizationID().isEmpty()) { + A1.append(new ByteArray(":" + getAuthenticationID())); + } + ByteArray A2 = new ByteArray("AUTHENTICATE:" + digestURI); + + String responseValue = Hexify.hexify(crypto.getMD5Hash(new ByteArray( + Hexify.hexify(crypto.getMD5Hash(A1)) + ":" + + challenge.getValue("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" + + Hexify.hexify(crypto.getMD5Hash(A2))))); + + + DIGESTMD5Properties response = new DIGESTMD5Properties(); + response.setValue("username", getAuthenticationID()); + if (!realm.isEmpty()) { + response.setValue("realm", realm); + } + response.setValue("nonce", challenge.getValue("nonce")); + response.setValue("cnonce", cnonce); + response.setValue("nc", "00000001"); + response.setValue("qop", qop); + response.setValue("digest-uri", digestURI); + response.setValue("charset", "utf-8"); + response.setValue("response", responseValue); + if (!getAuthorizationID().isEmpty()) { + response.setValue("authzid", getAuthorizationID()); + } + return new SafeByteArray(response.serialize()); + } + else { + return null; + } + } + + public boolean setChallenge(final ByteArray challengeData) { + if (Step.Initial.equals(step)) { + if (challengeData == null) { + return false; + } + challenge = DIGESTMD5Properties.parse(challengeData); + + // Sanity checks + if (challenge.getValue("nonce") == null) { + return false; + } + if (challenge.getValue("charset") ==null || !(challenge.getValue("charset").equals("utf-8"))) { + return false; + } + step = Step.Response; + return true; + } + else { + step = Step.Final; + // TODO: Check RSPAuth + return true; + } + } +}
\ No newline at end of file |