0
|
1 package com.five_ten_sg.connectbot.service;
|
|
2
|
|
3 import java.io.IOException;
|
|
4 import java.security.SecureRandom;
|
|
5 import java.security.KeyPair;
|
|
6 import java.security.PrivateKey;
|
|
7 import java.security.PublicKey;
|
|
8 import java.security.interfaces.DSAPrivateKey;
|
|
9 import java.security.interfaces.DSAPublicKey;
|
|
10 import java.security.interfaces.ECPrivateKey;
|
|
11 import java.security.interfaces.ECPublicKey;
|
|
12 import java.security.interfaces.RSAPrivateKey;
|
|
13 import java.security.interfaces.RSAPublicKey;
|
|
14 import java.util.HashMap;
|
|
15 import java.util.Map;
|
|
16 import java.util.Map.Entry;
|
|
17 import java.util.concurrent.locks.Condition;
|
|
18 import java.util.concurrent.locks.Lock;
|
|
19 import java.util.concurrent.locks.ReentrantLock;
|
|
20
|
|
21 import com.five_ten_sg.connectbot.service.TerminalManager;
|
|
22 import com.five_ten_sg.connectbot.service.TerminalManager.KeyHolder;
|
|
23 import android.app.Service;
|
|
24 import android.content.ComponentName;
|
|
25 import android.content.Intent;
|
|
26 import android.content.ServiceConnection;
|
|
27 import android.os.IBinder;
|
|
28 import android.os.RemoteException;
|
|
29 import android.util.Log;
|
|
30
|
|
31 import com.madgag.ssh.android.authagent.AndroidAuthAgent;
|
|
32 import com.trilead.ssh2.signature.DSASHA1Verify;
|
|
33 import com.trilead.ssh2.signature.ECDSASHA2Verify;
|
|
34 import com.trilead.ssh2.signature.RSASHA1Verify;
|
|
35
|
|
36 public class AuthAgentService extends Service {
|
|
37 private static final String TAG = "ConnectBot.AuthAgentService";
|
|
38 protected TerminalManager manager;
|
|
39 final Lock lock = new ReentrantLock();
|
|
40 final Condition managerReady = lock.newCondition();
|
|
41
|
|
42 private ServiceConnection connection = new ServiceConnection() {
|
|
43 public void onServiceConnected(ComponentName className, IBinder service) {
|
|
44 Log.d(TAG, "Terminal manager available! Hurrah");
|
|
45 manager = ((TerminalManager.TerminalBinder) service).getService();
|
|
46 lock.lock();
|
|
47
|
|
48 try {
|
|
49 managerReady.signal();
|
|
50 }
|
|
51 finally {
|
|
52 lock.unlock();
|
|
53 }
|
|
54 }
|
|
55 public void onServiceDisconnected(ComponentName className) {
|
|
56 manager = null;
|
|
57 Log.d(TAG, "Terminal manager gone...");
|
|
58 }
|
|
59 };
|
|
60
|
|
61 @Override
|
|
62 public IBinder onBind(Intent intent) {
|
|
63 Log.d(TAG, "onBind() called");
|
|
64 bindService(new Intent(this, TerminalManager.class), connection, BIND_AUTO_CREATE);
|
|
65 return mBinder;
|
|
66 }
|
|
67
|
|
68 private final AndroidAuthAgent.Stub mBinder = new AndroidAuthAgent.Stub() {
|
|
69 public Map getIdentities() throws RemoteException {
|
|
70 Log.d(TAG, "getIdentities() called");
|
|
71 waitForTerminalManager();
|
|
72 Log.d(TAG, "getIdentities() manager.loadedKeypairs : " + manager.loadedKeypairs);
|
|
73 return sshEncodedPubKeysFrom(manager.loadedKeypairs);
|
|
74 }
|
|
75 public byte[] sign(byte[] publicKey, byte[] data) throws RemoteException {
|
|
76 Log.d(TAG, "sign() called");
|
|
77 waitForTerminalManager();
|
|
78 KeyPair pair = keyPairFor(publicKey);
|
|
79 Log.d(TAG, "sign() - signing keypair found : " + pair);
|
|
80
|
|
81 if (pair == null) {
|
|
82 return null;
|
|
83 }
|
|
84
|
|
85 PrivateKey privKey = pair.getPrivate();
|
|
86
|
|
87 if (privKey instanceof RSAPrivateKey) {
|
|
88 return sshEncodedSignatureFor(data, (RSAPrivateKey) privKey);
|
|
89 }
|
|
90 else if (privKey instanceof DSAPrivateKey) {
|
|
91 return sshEncodedSignatureFor(data, (DSAPrivateKey) privKey);
|
|
92 }
|
|
93
|
|
94 return null;
|
|
95 }
|
|
96 private void waitForTerminalManager() throws RemoteException {
|
|
97 lock.lock();
|
|
98
|
|
99 try {
|
|
100 while (manager == null) {
|
|
101 Log.d(TAG, "Waiting for TerminalManager...");
|
|
102 managerReady.await();
|
|
103 }
|
|
104 }
|
|
105 catch (InterruptedException e) {
|
|
106 throw new RemoteException();
|
|
107 }
|
|
108 finally {
|
|
109 lock.unlock();
|
|
110 }
|
|
111
|
|
112 Log.d(TAG, "Got TerminalManager : " + manager);
|
|
113 }
|
|
114 private Map<String, byte[]> sshEncodedPubKeysFrom(Map<String, KeyHolder> keypairs) {
|
|
115 Map<String, byte[]> pubkeys = new HashMap<String, byte[]> (keypairs.size());
|
|
116
|
|
117 for (Entry<String, KeyHolder> entry : keypairs.entrySet()) {
|
|
118 byte[] encodedKey = sshEncodedPubKeyFrom(entry.getValue().pair);
|
|
119
|
|
120 if (encodedKey != null) {
|
|
121 pubkeys.put(entry.getKey(), encodedKey);
|
|
122 }
|
|
123 }
|
|
124
|
|
125 return pubkeys;
|
|
126 }
|
|
127 private byte[] sshEncodedPubKeyFrom(KeyPair pair) {
|
|
128 try {
|
|
129 PrivateKey privKey = pair.getPrivate();
|
|
130
|
|
131 if (privKey instanceof RSAPrivateKey) {
|
|
132 RSAPublicKey pubkey = (RSAPublicKey)pair.getPublic();
|
|
133 return RSASHA1Verify.encodeSSHRSAPublicKey(pubkey);
|
|
134 }
|
|
135 else if (privKey instanceof DSAPrivateKey) {
|
|
136 DSAPublicKey pubkey = (DSAPublicKey)pair.getPublic();
|
|
137 return DSASHA1Verify.encodeSSHDSAPublicKey(pubkey);
|
|
138 }
|
|
139 }
|
|
140 catch (IOException e) {
|
|
141 Log.e(TAG, "Couldn't encode " + pair, e);
|
|
142 }
|
|
143
|
|
144 return null;
|
|
145 }
|
|
146 private byte[] sshEncodedSignatureFor(byte[] data, RSAPrivateKey privKey) {
|
|
147 try {
|
|
148 byte[] signature = RSASHA1Verify.generateSignature(data, privKey);
|
|
149 return RSASHA1Verify.encodeSSHRSASignature(signature);
|
|
150 }
|
|
151 catch (IOException e) {
|
|
152 throw new RuntimeException(e);
|
|
153 }
|
|
154 }
|
|
155 private byte[] sshEncodedSignatureFor(byte[] data, DSAPrivateKey privKey) {
|
|
156 try {
|
|
157 byte[] signature = DSASHA1Verify.generateSignature(data, privKey, new SecureRandom());
|
|
158 return DSASHA1Verify.encodeSSHDSASignature(signature);
|
|
159 }
|
|
160 catch (IOException e) {
|
|
161 throw new RuntimeException(e);
|
|
162 }
|
|
163 }
|
|
164 private KeyPair keyPairFor(byte[] publicKey) {
|
|
165 String nickname = manager.getKeyNickname(publicKey);
|
|
166
|
|
167 if (nickname == null) {
|
|
168 Log.w(TAG, "No key-pair found for public-key.");
|
|
169 return null;
|
|
170 }
|
|
171
|
|
172 // check manager.loadedKeypairs.get(nickname).bean.isConfirmUse() and promptForPubkeyUse(nickname) ?
|
|
173 return manager.getKey(nickname);
|
|
174 }
|
|
175 };
|
|
176 }
|