mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-09 22:51:22 +00:00
GUACAMOLE-136: Implement basic support for verifying user identity using Duo.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
package com.duosecurity.duoweb;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public final class DuoWeb {
|
||||
private static final String DUO_PREFIX = "TX";
|
||||
private static final String APP_PREFIX = "APP";
|
||||
private static final String AUTH_PREFIX = "AUTH";
|
||||
|
||||
private static final int DUO_EXPIRE = 300;
|
||||
private static final int APP_EXPIRE = 3600;
|
||||
|
||||
private static final int IKEY_LEN = 20;
|
||||
private static final int SKEY_LEN = 40;
|
||||
private static final int AKEY_LEN = 40;
|
||||
|
||||
public static final String ERR_USER = "ERR|The username passed to sign_request() is invalid.";
|
||||
public static final String ERR_IKEY = "ERR|The Duo integration key passed to sign_request() is invalid.";
|
||||
public static final String ERR_SKEY = "ERR|The Duo secret key passed to sign_request() is invalid.";
|
||||
public static final String ERR_AKEY = "ERR|The application secret key passed to sign_request() must be at least " + AKEY_LEN + " characters.";
|
||||
public static final String ERR_UNKNOWN = "ERR|An unknown error has occurred.";
|
||||
|
||||
public static String signRequest(final String ikey, final String skey, final String akey, final String username) {
|
||||
return signRequest(ikey, skey, akey, username, System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
public static String signRequest(final String ikey, final String skey, final String akey, final String username, final long time) {
|
||||
final String duo_sig;
|
||||
final String app_sig;
|
||||
|
||||
if (username.equals("")) {
|
||||
return ERR_USER;
|
||||
}
|
||||
if (username.indexOf('|') != -1) {
|
||||
return ERR_USER;
|
||||
}
|
||||
if (ikey.equals("") || ikey.length() != IKEY_LEN) {
|
||||
return ERR_IKEY;
|
||||
}
|
||||
if (skey.equals("") || skey.length() != SKEY_LEN) {
|
||||
return ERR_SKEY;
|
||||
}
|
||||
if (akey.equals("") || akey.length() < AKEY_LEN) {
|
||||
return ERR_AKEY;
|
||||
}
|
||||
|
||||
try {
|
||||
duo_sig = signVals(skey, username, ikey, DUO_PREFIX, DUO_EXPIRE, time);
|
||||
app_sig = signVals(akey, username, ikey, APP_PREFIX, APP_EXPIRE, time);
|
||||
} catch (Exception e) {
|
||||
return ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
return duo_sig + ":" + app_sig;
|
||||
}
|
||||
|
||||
public static String verifyResponse(final String ikey, final String skey, final String akey, final String sig_response)
|
||||
throws DuoWebException, NoSuchAlgorithmException, InvalidKeyException, IOException {
|
||||
return verifyResponse(ikey, skey, akey, sig_response, System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
public static String verifyResponse(final String ikey, final String skey, final String akey, final String sig_response, final long time)
|
||||
throws DuoWebException, NoSuchAlgorithmException, InvalidKeyException, IOException {
|
||||
String auth_user = null;
|
||||
String app_user = null;
|
||||
|
||||
final String[] sigs = sig_response.split(":");
|
||||
final String auth_sig = sigs[0];
|
||||
final String app_sig = sigs[1];
|
||||
|
||||
auth_user = parseVals(skey, auth_sig, AUTH_PREFIX, ikey, time);
|
||||
app_user = parseVals(akey, app_sig, APP_PREFIX, ikey, time);
|
||||
|
||||
if (!auth_user.equals(app_user)) {
|
||||
throw new DuoWebException("Authentication failed.");
|
||||
}
|
||||
|
||||
return auth_user;
|
||||
}
|
||||
|
||||
private static String signVals(final String key, final String username, final String ikey, final String prefix, final int expire, final long time)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
final long expire_ts = time + expire;
|
||||
final String exp = Long.toString(expire_ts);
|
||||
|
||||
final String val = username + "|" + ikey + "|" + exp;
|
||||
final String cookie = prefix + "|" + Base64.encodeBytes(val.getBytes());
|
||||
final String sig = Util.hmacSign(key, cookie);
|
||||
|
||||
return cookie + "|" + sig;
|
||||
}
|
||||
|
||||
private static String parseVals(final String key, final String val, final String prefix, final String ikey, final long time)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException, IOException, DuoWebException {
|
||||
|
||||
final String[] parts = val.split("\\|");
|
||||
if (parts.length != 3) {
|
||||
throw new DuoWebException("Invalid response");
|
||||
}
|
||||
|
||||
final String u_prefix = parts[0];
|
||||
final String u_b64 = parts[1];
|
||||
final String u_sig = parts[2];
|
||||
|
||||
final String sig = Util.hmacSign(key, u_prefix + "|" + u_b64);
|
||||
if (!Util.hmacSign(key, sig).equals(Util.hmacSign(key, u_sig))) {
|
||||
throw new DuoWebException("Invalid response");
|
||||
}
|
||||
|
||||
if (!u_prefix.equals(prefix)) {
|
||||
throw new DuoWebException("Invalid response");
|
||||
}
|
||||
|
||||
final byte[] decoded = Base64.decode(u_b64);
|
||||
final String cookie = new String(decoded);
|
||||
|
||||
final String[] cookie_parts = cookie.split("\\|");
|
||||
if (cookie_parts.length != 3) {
|
||||
throw new DuoWebException("Invalid response");
|
||||
}
|
||||
final String username = cookie_parts[0];
|
||||
final String u_ikey = cookie_parts[1];
|
||||
final String expire = cookie_parts[2];
|
||||
|
||||
if (!u_ikey.equals(ikey)) {
|
||||
throw new DuoWebException("Invalid response");
|
||||
}
|
||||
|
||||
final long expire_ts = Long.parseLong(expire);
|
||||
if (time >= expire_ts) {
|
||||
throw new DuoWebException("Transaction has expired. Please check that the system time is correct.");
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package com.duosecurity.duoweb;
|
||||
|
||||
public class DuoWebException extends Exception {
|
||||
|
||||
public DuoWebException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package com.duosecurity.duoweb;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class Util {
|
||||
public static String hmacSign(String skey, String data)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
SecretKeySpec key = new SecretKeySpec(skey.getBytes(), "HmacSHA1");
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
mac.init(key);
|
||||
byte[] raw = mac.doFinal(data.getBytes());
|
||||
return bytesToHex(raw);
|
||||
}
|
||||
|
||||
public static String bytesToHex(byte[] b) {
|
||||
String result = "";
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user