An optional secure channel can be created for authentication to ensure credentials are encrypted
when sent to the server. To implement a secure authentication channel: - Create a key pair using the com.threerings.presents.tool.KeyPairGen tool. You will need to distribute the public key with your client, and the private key with the server. - On your server during initialization, use PresentsConnectionManager.setPrivateKey to set the private key. It can take the key string gerenated by KeyPairGen. This will return true if the key was sucessfully set and the server supports the encryption necessary. - On your client before authenticating, use Client.setPublicKey to set the public key. As with setPrivateKey, it will return true if the client supports the encryption necessary. That's it! You should now be authenticating over a secure encrypted channel. The server can still accept unsecured authentication attempts (for the purpose of telling the client it needs a new version to get the server's public key). A failure to decrypt the client credientials on the server will return a new "m.failed_to_secure" authentication code. Handshake process: - The client generates a random 128-bit key, and encodes it with the public key using a 32-bit salt (PublicKeyCredentials). This is sent to the server as a SecureRequest. - The server decrypts the i128-bit key using its private key and verifies it against the salt. If verification fails, a failed secure response is returned and the client will authenticate over a clear channel. If verification succeeds, the server generates a random 128-bit AES key and encodes it with the 128-bit key sent from the client. This is sent back to the client as a SecureResponse. - The client will decode the AES key sent from the server (using the random key it generated at the start of the handshake). Using the AES key, the client will encrypt their credentials using an AESAuthRequest and send it to the server. - The server can now decrypt the credentials from the client and pass the AESAuthRequest to the configured authenticators to complete authentication. If the server fails to decrypt the credentials a "m.failed_to_secure" authentication code is returned to the client. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6477 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -25,6 +25,7 @@ import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
@@ -51,12 +52,16 @@ import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.UnreliableObjectInputStream;
|
||||
import com.threerings.io.UnreliableObjectOutputStream;
|
||||
|
||||
import com.threerings.presents.net.AESAuthRequest;
|
||||
import com.threerings.presents.net.AuthRequest;
|
||||
import com.threerings.presents.net.AuthResponse;
|
||||
import com.threerings.presents.net.AuthResponseData;
|
||||
import com.threerings.presents.net.DownstreamMessage;
|
||||
import com.threerings.presents.net.LogoffRequest;
|
||||
import com.threerings.presents.net.PingRequest;
|
||||
import com.threerings.presents.net.PublicKeyCredentials;
|
||||
import com.threerings.presents.net.SecureRequest;
|
||||
import com.threerings.presents.net.SecureResponse;
|
||||
import com.threerings.presents.net.TransmitDatagramsRequest;
|
||||
import com.threerings.presents.net.Transport;
|
||||
import com.threerings.presents.net.UpstreamMessage;
|
||||
@@ -616,13 +621,40 @@ public class BlockingCommunicator extends Communicator
|
||||
// connect to the server
|
||||
connect();
|
||||
|
||||
// construct an auth request and send it
|
||||
sendMessage(new AuthRequest(_client.getCredentials(), _client.getVersion(),
|
||||
_client.getBootGroups()));
|
||||
// If a public key is specified, we'll attempt to establish a secure authentication
|
||||
// channel
|
||||
PublicKey key = _client.getPublicKey();
|
||||
AuthResponse response = null;
|
||||
if (key != null) {
|
||||
PublicKeyCredentials pkcreds = new PublicKeyCredentials(key);
|
||||
sendMessage(new SecureRequest(pkcreds, _client.getVersion()));
|
||||
|
||||
// now wait for the handshake
|
||||
log.debug("Waiting for secure response.");
|
||||
|
||||
response = (AuthResponse)receiveMessage();
|
||||
// If we've recieved a secure response, proceed with authentication
|
||||
if (response instanceof SecureResponse) {
|
||||
sendMessage(((SecureResponse)response).createAuthRequest(
|
||||
pkcreds, _client.getCredentials(),
|
||||
_client.getVersion(), _client.getBootGroups()));
|
||||
|
||||
// now wait for the auth response
|
||||
log.debug("Waiting for auth response.");
|
||||
response = (AuthResponse)receiveMessage();
|
||||
}
|
||||
|
||||
} else {
|
||||
// construct an auth request and send it
|
||||
sendMessage(new AuthRequest(
|
||||
_client.getCredentials(), _client.getVersion(), _client.getBootGroups()));
|
||||
|
||||
// now wait for the auth response
|
||||
log.debug("Waiting for auth response.");
|
||||
response = (AuthResponse)receiveMessage();
|
||||
}
|
||||
gotAuthResponse(response);
|
||||
|
||||
// now wait for the auth response
|
||||
log.debug("Waiting for auth response.");
|
||||
gotAuthResponse((AuthResponse)receiveMessage());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug("Logon failed: " + e);
|
||||
|
||||
@@ -23,6 +23,8 @@ package com.threerings.presents.client;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import com.samskivert.util.Interval;
|
||||
@@ -45,6 +47,7 @@ import com.threerings.presents.net.Credentials;
|
||||
import com.threerings.presents.net.PingRequest;
|
||||
import com.threerings.presents.net.PongResponse;
|
||||
import com.threerings.presents.net.ThrottleUpdatedMessage;
|
||||
import com.threerings.presents.util.SecureUtil;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
@@ -198,6 +201,43 @@ public class Client
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key with which this client is currently configured to create a secure
|
||||
* authentication channel to the server.
|
||||
*/
|
||||
public PublicKey getPublicKey ()
|
||||
{
|
||||
return _publicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the public key that will be used by this client to create a secure authentication
|
||||
* channel with the server if the ciphers are supported. This should be done before any call
|
||||
* to <code>logon</code>.
|
||||
*
|
||||
* @return true if the key is set
|
||||
*/
|
||||
public boolean setPublicKey (PublicKey key)
|
||||
{
|
||||
if (SecureUtil.ciphersSupported(key)) {
|
||||
_publicKey = key;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the public key that will be used by this client to create a secure authentication
|
||||
* channel with the server if the ciphers are supported. This should be done before any call
|
||||
* to <code>logon</code>.
|
||||
*
|
||||
* @return true if the key is set
|
||||
*/
|
||||
public boolean setPublicKey (String key)
|
||||
{
|
||||
return key == null ? false : setPublicKey(SecureUtil.stringToRSAPublicKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version string configured for this client.
|
||||
*/
|
||||
@@ -986,6 +1026,9 @@ public class Client
|
||||
/** The data associated with our authentication response. */
|
||||
protected AuthResponseData _authData;
|
||||
|
||||
/** Our public key. */
|
||||
protected PublicKey _publicKey;
|
||||
|
||||
/** The unique id of our connection. */
|
||||
protected int _connectionId = -1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user