summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/isode/stroke/sasl/DIGESTMD5ClientAuthenticator.java')
-rw-r--r--src/com/isode/stroke/sasl/DIGESTMD5ClientAuthenticator.java115
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