aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhsm <hsm@lamia.panaceas.james.local>2016-05-29 04:17:53 +0100
committerhsm <hsm@lamia.panaceas.james.local>2016-05-29 17:13:25 +0100
commitf8fffe5e29f55856b93b5e21f1a672bb1a0fec40 (patch)
tree298bb1053031648aaac04931061ae98e041498aa
parent362e6695b48ddf3d37fea426b4f48b2d0f07d9a0 (diff)
downloadsshlib-f8fffe5e29f55856b93b5e21f1a672bb1a0fec40.tar.gz
sshlib-f8fffe5e29f55856b93b5e21f1a672bb1a0fec40.tar.bz2
sshlib-f8fffe5e29f55856b93b5e21f1a672bb1a0fec40.zip
Add support for auth with open-keychain
-rw-r--r--sshlib/src/main/AndroidManifest.xml7
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java33
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java59
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java3
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSAPrivateKey.java72
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSASHA1Verify.java175
6 files changed, 348 insertions, 1 deletions
diff --git a/sshlib/src/main/AndroidManifest.xml b/sshlib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d15f4a6
--- /dev/null
+++ b/sshlib/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.connectbot.sshlib">
+
+ <application/>
+
+</manifest>
diff --git a/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java b/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java
index dfafcbd..117ed57 100644
--- a/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java
+++ b/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java
@@ -33,6 +33,8 @@ import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.ECDSASHA2Verify;
import com.trilead.ssh2.signature.Ed25519Verify;
import com.trilead.ssh2.signature.RSASHA1Verify;
+import com.trilead.ssh2.signature.TokenRSAPrivateKey;
+import com.trilead.ssh2.signature.TokenRSASHA1Verify;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;
@@ -246,6 +248,37 @@ public class AuthenticationManager implements MessageHandler
tm.sendMessage(ua.getPayload());
}
+ else if (key instanceof TokenRSAPrivateKey)
+ {
+ TokenRSAPrivateKey pk = (TokenRSAPrivateKey) key;
+
+ byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic());
+
+ TypesWriter tw = new TypesWriter();
+ {
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString("ssh-rsa");
+ tw.writeString(pk_enc, 0, pk_enc.length);
+ }
+
+ byte[] msg = tw.getBytes();
+
+ byte[] ds = TokenRSASHA1Verify.generateSignature(msg, pk);
+
+ byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ "ssh-rsa", pk_enc, rsa_sig_enc);
+
+ tm.sendMessage(ua.getPayload());
+ }
else if (key instanceof ECPrivateKey)
{
ECPrivateKey pk = (ECPrivateKey) key;
diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
index 5c0c2fd..09b875e 100644
--- a/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
+++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
@@ -30,6 +30,7 @@ import com.trilead.ssh2.crypto.cipher.CBCMode;
import com.trilead.ssh2.crypto.cipher.DES;
import com.trilead.ssh2.crypto.cipher.DESede;
import com.trilead.ssh2.signature.ECDSASHA2Verify;
+import com.trilead.ssh2.signature.TokenRSAPrivateKey;
/**
* PEM Support.
@@ -42,6 +43,7 @@ public class PEMDecoder
public static final int PEM_RSA_PRIVATE_KEY = 1;
public static final int PEM_DSA_PRIVATE_KEY = 2;
public static final int PEM_EC_PRIVATE_KEY = 3;
+ public static final int PEM_RSA_TOKEN_PRIVATE_KEY = 4;
private static final int hexToInt(char c)
{
@@ -186,6 +188,12 @@ public class PEMDecoder
ps.pemType = PEM_EC_PRIVATE_KEY;
break;
}
+
+ if (line.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
+ endLine = "-----END RSA PUBLIC KEY-----";
+ ps.pemType = PEM_RSA_TOKEN_PRIVATE_KEY;
+ break;
+ }
}
while (true)
@@ -224,6 +232,12 @@ public class PEMDecoder
ps.dekInfo = values;
continue;
}
+
+ if ("Private-Key-ID:".equals(name))
+ {
+ ps.private_key_id = values;
+ continue;
+ }
/* Ignore line */
}
@@ -468,9 +482,54 @@ public class PEMDecoder
return generateKeyPair("EC", privSpec, pubSpec);
}
+ if (ps.pemType == PEM_RSA_TOKEN_PRIVATE_KEY)
+ {
+
+ if (ps.private_key_id == null) {
+ throw new IOException("No Private-Key-ID: line in stream.");
+ }
+ if (ps.private_key_id.length != 1) {
+ throw new IOException("No Private-Key-ID: line in stream.");
+ }
+
+ SimpleDERReader dr = new SimpleDERReader(ps.data);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in RSA PUBLIC KEY DER stream.");
+
+ dr.resetInput(seq);
+
+ BigInteger n = dr.readInt();
+ BigInteger e = dr.readInt();
+
+ RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(n, e);
+
+ return generateTokenKeyPair("RSA", new TokenRSAPrivateKey(ps.private_key_id[0]), pubSpec);
+ }
+
throw new IOException("PEM problem: it is of unknown type");
}
+
+ private static KeyPair generateTokenKeyPair(String algorithm, PrivateKey priv_key, KeySpec pubSpec)
+ throws IOException {
+ try {
+ final KeyFactory kf = KeyFactory.getInstance(algorithm);
+ final PublicKey pubKey = kf.generatePublic(pubSpec);
+ final PrivateKey privKey = priv_key;
+ return new KeyPair(pubKey, privKey);
+ } catch (NoSuchAlgorithmException ex) {
+ IOException ioex = new IOException();
+ ioex.initCause(ex);
+ throw ioex;
+ } catch (InvalidKeySpecException ex) {
+ IOException ioex = new IOException("invalid keyspec");
+ ioex.initCause(ex);
+ throw ioex;
+ }
+ }
/**
* Generate a {@code KeyPair} given an {@code algorithm} and {@code KeySpec}.
*/
diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java
index 83fb799..0aeb2eb 100644
--- a/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java
+++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java
@@ -12,6 +12,7 @@ public class PEMStructure
{
public int pemType;
String dekInfo[];
+ String private_key_id[];
String procType[];
public byte[] data;
-} \ No newline at end of file
+}
diff --git a/sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSAPrivateKey.java b/sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSAPrivateKey.java
new file mode 100644
index 0000000..4438cab
--- /dev/null
+++ b/sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSAPrivateKey.java
@@ -0,0 +1,72 @@
+
+package com.trilead.ssh2.signature;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.PrivateKey;
+import java.security.spec.RSAPublicKeySpec;
+
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+
+
+public class TokenRSAPrivateKey implements PrivateKey
+{
+ private long key_id;
+
+ public TokenRSAPrivateKey (String s)
+ {
+ key_id = new BigInteger (s, 16).longValue();
+ }
+
+ public TokenRSAPrivateKey (long l)
+ {
+ key_id = l;
+ }
+
+ public long getKeyId()
+ {
+ return key_id;
+ }
+
+ private void writeObject (ObjectOutputStream stream) throws IOException
+ {
+ throw new IOException();
+ }
+
+ public void readObject (ObjectInputStream stream) throws IOException
+ {
+ throw new IOException();
+ }
+
+ public void readObjectNoData() throws ObjectStreamException
+ {
+ throw new ObjectStreamException() {};
+ }
+
+ public String getAlgorithm()
+ {
+ return "TokenRSA";
+ }
+
+ public String getFormat()
+ {
+ return "None";
+ }
+
+ public byte[] getEncoded()
+ {
+ return new byte[0];
+ }
+}
+
diff --git a/sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSASHA1Verify.java b/sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSASHA1Verify.java
new file mode 100644
index 0000000..d8b95fa
--- /dev/null
+++ b/sshlib/src/main/java/com/trilead/ssh2/signature/TokenRSASHA1Verify.java
@@ -0,0 +1,175 @@
+
+package com.trilead.ssh2.signature;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+
+
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+
+import android.app.Activity;
+import android.content.IntentSender;
+import android.content.Intent;
+import android.app.PendingIntent;
+
+
+
+import org.openintents.openpgp.IOpenPgpService2;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
+import org.openintents.openpgp.OpenPgpError;
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.openintents.openpgp.util.OpenPgpServiceConnection;
+import org.openintents.openpgp.util.OpenPgpUtils;
+
+
+/**
+ * TokenRSASHA1Verify.
+ *
+ * @author James McKenzie
+ */
+public class TokenRSASHA1Verify
+{
+ private static final Object lock = new Object();
+
+ private static final Logger log = Logger.getLogger (TokenRSASHA1Verify.class);
+ private static final int pending_intent_code = 28674;
+
+ static private Activity activity;
+ static private OpenPgpServiceConnection mServiceConnection;
+
+ static private boolean async_semaphore = false;
+ static private boolean async_abort = false;
+ static private Intent async_intent;
+
+ public static void open (Activity _activity)
+ {
+ activity = _activity;
+
+ if (activity == null)
+ return;
+
+ mServiceConnection = new OpenPgpServiceConnection (activity, "org.sufficientlysecure.keychain");
+ mServiceConnection.bindToService();
+ }
+
+
+ public static void callback (int requestCode, int resultCode, Intent intent)
+ {
+ if (requestCode != pending_intent_code) return;
+
+ synchronized (lock) {
+ if (resultCode == Activity.RESULT_OK) {
+ async_intent = intent;
+ async_abort = false;
+ } else
+ async_abort = true;
+
+ async_semaphore = true;
+
+ lock.notify();
+ }
+ }
+
+ public static byte[] generateSignature (byte[] message, TokenRSAPrivateKey pk) throws IOException
+ {
+ byte [] fail = new byte[0];
+ long key_id = pk.getKeyId();
+
+ if ((activity == null) || (mServiceConnection == null)) return fail;
+
+ Intent data = new Intent();
+ data.setAction (OpenPgpApi.ACTION_SSH_AUTH);
+ data.putExtra (OpenPgpApi.EXTRA_SIGN_KEY_ID, key_id);
+
+ InputStream is = new ByteArrayInputStream (message);
+
+ OpenPgpApi api = new OpenPgpApi (activity, mServiceConnection.getService());
+ Intent result = api.executeApi (data, is, null);
+
+
+ int result_code;
+
+ do {
+ result_code = result.getIntExtra (OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
+
+ if (result_code == OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED) {
+
+ synchronized (lock) {
+ async_semaphore = false;
+ async_abort = true;
+
+ PendingIntent pi = result.getParcelableExtra (OpenPgpApi.RESULT_INTENT);
+
+ try {
+ activity.startIntentSenderForResult (pi.getIntentSender(), pending_intent_code, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ return fail;
+ }
+
+ try {
+ while (async_semaphore == false)
+ lock.wait();
+ } catch (InterruptedException e) { }
+
+ if (async_abort)
+ return fail;
+
+ data = async_intent;
+ }
+
+ is = new ByteArrayInputStream (message);
+ result = api.executeApi (data, is, null);
+
+ } else
+ break;
+
+ } while (true);
+
+ switch (result_code) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS: {
+
+ byte [] output = result.getByteArrayExtra (OpenPgpApi.RESULT_DETACHED_SIGNATURE);
+
+ if (output == null)
+ return fail;
+
+ return output;
+ }
+
+ case OpenPgpApi.RESULT_CODE_ERROR: {
+ //OpenPgpError error = result.getParcelableExtra (OpenPgpApi.RESULT_ERROR);
+ return fail;
+ }
+ }
+
+ return fail;
+
+ }
+
+ public static void close()
+ {
+ if (mServiceConnection != null)
+ mServiceConnection.unbindFromService();
+
+ activity = null;
+ }
+
+}