tls-psk: second steps
This commit is contained in:
@@ -22,19 +22,19 @@ import java.io.BufferedReader;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Collections;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import android.net.PskKeyManager;
|
import android.net.PskKeyManager;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build.VERSION_CODES;
|
import android.os.Build.VERSION_CODES;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -42,7 +42,6 @@ import javax.net.ssl.KeyManager;
|
|||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLServerSocket;
|
import javax.net.ssl.SSLServerSocket;
|
||||||
import javax.net.ssl.SSLSocket;
|
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
@@ -50,9 +49,8 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||||||
|
|
||||||
@RequiresApi(api = VERSION_CODES.LOLLIPOP)
|
@RequiresApi(api = VERSION_CODES.LOLLIPOP)
|
||||||
public class KeyTransferClientInteractor {
|
public class KeyTransferClientInteractor {
|
||||||
private static final int SHOW_CONNECTION_DETAILS = 1;
|
|
||||||
private static final int CONNECTION_ESTABLISHED = 2;
|
private static final int CONNECTION_ESTABLISHED = 2;
|
||||||
public static final int CONNECTION_LOST = 3;
|
private static final int CONNECTION_LOST = 3;
|
||||||
|
|
||||||
|
|
||||||
private Thread socketThread;
|
private Thread socketThread;
|
||||||
@@ -64,6 +62,11 @@ public class KeyTransferClientInteractor {
|
|||||||
public void connectToServer(final String connectionDetails, KeyTransferClientCallback callback) {
|
public void connectToServer(final String connectionDetails, KeyTransferClientCallback callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(connectionDetails);
|
||||||
|
final byte[] presharedKey = Base64.decode(uri.getUserInfo(), Base64.URL_SAFE | Base64.NO_PADDING);
|
||||||
|
final String host = uri.getHost();
|
||||||
|
final int port = uri.getPort();
|
||||||
|
|
||||||
handler = new Handler(Looper.getMainLooper());
|
handler = new Handler(Looper.getMainLooper());
|
||||||
socketThread = new Thread() {
|
socketThread = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
@@ -72,13 +75,10 @@ public class KeyTransferClientInteractor {
|
|||||||
Socket socket = null;
|
Socket socket = null;
|
||||||
BufferedReader bufferedReader = null;
|
BufferedReader bufferedReader = null;
|
||||||
try {
|
try {
|
||||||
int port = 1336;
|
PKM pskKeyManager = new PKM(presharedKey);
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
PKM pskKeyManager = new PKM();
|
|
||||||
SSLContext sslContext = null;
|
|
||||||
sslContext = SSLContext.getInstance("TLS");
|
|
||||||
sslContext.init(new KeyManager[] { pskKeyManager }, new TrustManager[0], null);
|
sslContext.init(new KeyManager[] { pskKeyManager }, new TrustManager[0], null);
|
||||||
socket = sslContext.getSocketFactory().createSocket(InetAddress.getByName(connectionDetails), port);
|
socket = sslContext.getSocketFactory().createSocket(InetAddress.getByName(host), port);
|
||||||
|
|
||||||
invokeListener(CONNECTION_ESTABLISHED, socket.getInetAddress().toString());
|
invokeListener(CONNECTION_ESTABLISHED, socket.getInetAddress().toString());
|
||||||
|
|
||||||
@@ -169,14 +169,20 @@ public class KeyTransferClientInteractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class PKM extends PskKeyManager implements KeyManager {
|
private static class PKM extends PskKeyManager implements KeyManager {
|
||||||
|
byte[] presharedKey;
|
||||||
|
|
||||||
|
private PKM(byte[] presharedKey) {
|
||||||
|
this.presharedKey = presharedKey;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey getKey(String identityHint, String identity, Socket socket) {
|
public SecretKey getKey(String identityHint, String identity, Socket socket) {
|
||||||
return new SecretKeySpec("swag".getBytes(), "AES");
|
return new SecretKeySpec(presharedKey, "AES");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
|
public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
|
||||||
return new SecretKeySpec("swag".getBytes(), "AES");
|
return new SecretKeySpec(presharedKey, "AES");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import java.net.Socket;
|
|||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -35,6 +37,7 @@ import android.os.Build.VERSION_CODES;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -72,16 +75,20 @@ public class KeyTransferServerInteractor {
|
|||||||
try {
|
try {
|
||||||
int port = 1336;
|
int port = 1336;
|
||||||
|
|
||||||
PKM pskKeyManager = new PKM();
|
byte[] presharedKey = generatePresharedKey();
|
||||||
|
PKM pskKeyManager = new PKM(presharedKey);
|
||||||
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
sslContext.init(new KeyManager[] { pskKeyManager }, new TrustManager[0], null);
|
sslContext.init(new KeyManager[] { pskKeyManager }, new TrustManager[0], null);
|
||||||
serverSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(port);
|
serverSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(port);
|
||||||
|
|
||||||
String qrCodeData = getIPAddress(true) + ":" + port + ":" + "swag";
|
String presharedKeyEncoded = Base64.encodeToString(presharedKey, Base64.URL_SAFE | Base64.NO_PADDING);
|
||||||
|
String qrCodeData = presharedKeyEncoded + "@" + getIPAddress(true) + ":" + port;
|
||||||
invokeListener(SHOW_CONNECTION_DETAILS, qrCodeData);
|
invokeListener(SHOW_CONNECTION_DETAILS, qrCodeData);
|
||||||
|
|
||||||
socket = serverSocket.accept();
|
socket = serverSocket.accept();
|
||||||
invokeListener(CONNECTION_ESTABLISHED, socket.getInetAddress().toString());
|
invokeListener(CONNECTION_ESTABLISHED, socket.getInetAddress().toString());
|
||||||
|
Arrays.fill(presharedKey, (byte) 0);
|
||||||
|
|
||||||
socket.setSoTimeout(500);
|
socket.setSoTimeout(500);
|
||||||
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||||
@@ -130,6 +137,12 @@ public class KeyTransferServerInteractor {
|
|||||||
socketThread.start();
|
socketThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] generatePresharedKey() {
|
||||||
|
byte[] presharedKey = new byte[16];
|
||||||
|
new SecureRandom().nextBytes(presharedKey);
|
||||||
|
return presharedKey;
|
||||||
|
}
|
||||||
|
|
||||||
public void stopServer() {
|
public void stopServer() {
|
||||||
if (socketThread != null) {
|
if (socketThread != null) {
|
||||||
socketThread.interrupt();
|
socketThread.interrupt();
|
||||||
@@ -156,6 +169,9 @@ public class KeyTransferServerInteractor {
|
|||||||
Runnable runnable = new Runnable() {
|
Runnable runnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
if (callback == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case SHOW_CONNECTION_DETAILS:
|
case SHOW_CONNECTION_DETAILS:
|
||||||
callback.onServerStarted(arg);
|
callback.onServerStarted(arg);
|
||||||
@@ -216,14 +232,20 @@ public class KeyTransferServerInteractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class PKM extends PskKeyManager implements KeyManager {
|
private static class PKM extends PskKeyManager implements KeyManager {
|
||||||
|
byte[] presharedKey;
|
||||||
|
|
||||||
|
private PKM(byte[] presharedKey) {
|
||||||
|
this.presharedKey = presharedKey;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey getKey(String identityHint, String identity, Socket socket) {
|
public SecretKey getKey(String identityHint, String identity, Socket socket) {
|
||||||
return new SecretKeySpec("swag".getBytes(), "AES");
|
return new SecretKeySpec(presharedKey, "AES");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
|
public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
|
||||||
return new SecretKeySpec("swag".getBytes(), "AES");
|
return new SecretKeySpec(presharedKey, "AES");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,12 @@ public class TransferPresenter implements KeyTransferServerCallback, KeyTransfer
|
|||||||
public void onClickScan() {
|
public void onClickScan() {
|
||||||
clearConnections();
|
clearConnections();
|
||||||
|
|
||||||
|
view.scanQrCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onQrCodeScanned(String qrCodeContent) {
|
||||||
keyTransferClientInteractor = new KeyTransferClientInteractor();
|
keyTransferClientInteractor = new KeyTransferClientInteractor();
|
||||||
keyTransferClientInteractor.connectToServer("10.100.40.126", this);
|
keyTransferClientInteractor.connectToServer(qrCodeContent, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearConnections() {
|
private void clearConnections() {
|
||||||
@@ -73,7 +77,7 @@ public class TransferPresenter implements KeyTransferServerCallback, KeyTransfer
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServerStarted(String qrCodeData) {
|
public void onServerStarted(String qrCodeData) {
|
||||||
Bitmap qrCodeBitmap = QrCodeUtils.getQRCodeBitmap(Uri.parse("pgp+transfer:" + qrCodeData), 0);
|
Bitmap qrCodeBitmap = QrCodeUtils.getQRCodeBitmap(Uri.parse("pgp+transfer://" + qrCodeData));
|
||||||
view.setQrImage(qrCodeBitmap);
|
view.setQrImage(qrCodeBitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,5 +97,7 @@ public class TransferPresenter implements KeyTransferServerCallback, KeyTransfer
|
|||||||
void showWaitingForConnection();
|
void showWaitingForConnection();
|
||||||
|
|
||||||
void setQrImage(Bitmap qrCode);
|
void setQrImage(Bitmap qrCode);
|
||||||
|
|
||||||
|
void scanQrCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
package org.sufficientlysecure.keychain.ui.transfer.view;
|
package org.sufficientlysecure.keychain.ui.transfer.view;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Build.VERSION_CODES;
|
import android.os.Build.VERSION_CODES;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -33,7 +35,9 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.ViewAnimator;
|
import android.widget.ViewAnimator;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.Intents;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.ui.QrCodeCaptureActivity;
|
||||||
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter;
|
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter;
|
||||||
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.TransferMvpView;
|
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.TransferMvpView;
|
||||||
|
|
||||||
@@ -42,10 +46,10 @@ import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.T
|
|||||||
public class TransferFragment extends Fragment implements TransferMvpView {
|
public class TransferFragment extends Fragment implements TransferMvpView {
|
||||||
public static final int VIEW_WAITING = 0;
|
public static final int VIEW_WAITING = 0;
|
||||||
public static final int VIEW_CONNECTED = 1;
|
public static final int VIEW_CONNECTED = 1;
|
||||||
|
public static final int REQUEST_CODE_SCAN = 1;
|
||||||
|
|
||||||
|
|
||||||
private ImageView vQrCodeImage;
|
private ImageView vQrCodeImage;
|
||||||
private View vScanButton;
|
|
||||||
private TransferPresenter presenter;
|
private TransferPresenter presenter;
|
||||||
private ViewAnimator vTransferAnimator;
|
private ViewAnimator vTransferAnimator;
|
||||||
private TextView vConnectionStatusText;
|
private TextView vConnectionStatusText;
|
||||||
@@ -60,8 +64,8 @@ public class TransferFragment extends Fragment implements TransferMvpView {
|
|||||||
vConnectionStatusText = (TextView) view.findViewById(R.id.connection_status);
|
vConnectionStatusText = (TextView) view.findViewById(R.id.connection_status);
|
||||||
|
|
||||||
vQrCodeImage = (ImageView) view.findViewById(R.id.qr_code_image);
|
vQrCodeImage = (ImageView) view.findViewById(R.id.qr_code_image);
|
||||||
vScanButton = view.findViewById(R.id.button_scan);
|
|
||||||
vScanButton.setOnClickListener(new OnClickListener() {
|
view.findViewById(R.id.button_scan).setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (presenter != null) {
|
if (presenter != null) {
|
||||||
@@ -121,4 +125,24 @@ public class TransferFragment extends Fragment implements TransferMvpView {
|
|||||||
});
|
});
|
||||||
vQrCodeImage.requestLayout();
|
vQrCodeImage.requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scanQrCode() {
|
||||||
|
Intent intent = new Intent(getActivity(), QrCodeCaptureActivity.class);
|
||||||
|
startActivityForResult(intent, REQUEST_CODE_SCAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_CODE_SCAN:
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
String qrCodeData = data.getStringExtra(Intents.Scan.RESULT);
|
||||||
|
presenter.onQrCodeScanned(qrCodeData);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ import java.util.Locale;
|
|||||||
*/
|
*/
|
||||||
public class QrCodeUtils {
|
public class QrCodeUtils {
|
||||||
|
|
||||||
|
public static Bitmap getQRCodeBitmap(final Uri uri) {
|
||||||
|
return getQRCodeBitmap(uri.toString(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
public static Bitmap getQRCodeBitmap(final Uri uri, final int size) {
|
public static Bitmap getQRCodeBitmap(final Uri uri, final int size) {
|
||||||
// for URIs we want alphanumeric encoding to save space, thus make everything upper case!
|
// for URIs we want alphanumeric encoding to save space, thus make everything upper case!
|
||||||
// zxing will then select Mode.ALPHANUMERIC internally
|
// zxing will then select Mode.ALPHANUMERIC internally
|
||||||
|
|||||||
Reference in New Issue
Block a user