diff options
| author | hsm <hsm@lamia.panaceas.james.local> | 2016-05-29 04:17:53 +0100 | 
|---|---|---|
| committer | hsm <hsm@lamia.panaceas.james.local> | 2016-05-29 17:13:25 +0100 | 
| commit | f8fffe5e29f55856b93b5e21f1a672bb1a0fec40 (patch) | |
| tree | 298bb1053031648aaac04931061ae98e041498aa | |
| parent | 362e6695b48ddf3d37fea426b4f48b2d0f07d9a0 (diff) | |
| download | sshlib-f8fffe5e29f55856b93b5e21f1a672bb1a0fec40.tar.gz sshlib-f8fffe5e29f55856b93b5e21f1a672bb1a0fec40.tar.bz2 sshlib-f8fffe5e29f55856b93b5e21f1a672bb1a0fec40.zip | |
Add support for auth with open-keychain
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; +  } + +} | 
