SSH.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. package de.tudarmstadt.informatik.hostage.protocol;
  2. import java.math.BigInteger;
  3. import java.nio.ByteBuffer;
  4. import java.security.KeyFactory;
  5. import java.security.KeyPair;
  6. import java.security.KeyPairGenerator;
  7. import java.security.MessageDigest;
  8. import java.security.PublicKey;
  9. import java.security.Signature;
  10. import java.security.interfaces.DSAPublicKey;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. import java.util.Random;
  14. import javax.crypto.KeyAgreement;
  15. import javax.crypto.interfaces.DHPublicKey;
  16. import javax.crypto.spec.DHParameterSpec;
  17. import javax.crypto.spec.DHPublicKeySpec;
  18. import de.tudarmstadt.informatik.hostage.wrapper.ByteArray;
  19. public final class SSH implements Protocol<ByteArray> {
  20. private enum STATE {
  21. NONE,
  22. SRVR_VERSION,
  23. CLNT_VERSION,
  24. KEX_INIT,
  25. DH_KEX_REP,
  26. CLOSED
  27. }
  28. private STATE connectionState = STATE.NONE;
  29. private String serverVersion = "SSH-2.0-";
  30. private String serverType = "OpenSSH_6.0p1 Debian-4";
  31. private final byte[] pDH = {
  32. (byte)0x00,
  33. (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
  34. (byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34,
  35. (byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1,
  36. (byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74,
  37. (byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22,
  38. (byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD,
  39. (byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B,
  40. (byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37,
  41. (byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45,
  42. (byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6,
  43. (byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B,
  44. (byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED,
  45. (byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5,
  46. (byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6,
  47. (byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE6,(byte)0x53,(byte)0x81,
  48. (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF
  49. };
  50. private final byte[] gDH = {0x02};
  51. private byte[] V_S = serverType.getBytes();
  52. private byte[] V_C;
  53. private byte[] I_S;
  54. private byte[] I_C;
  55. private byte[] eDH;
  56. private byte[] fDH;
  57. private byte[] kDH;
  58. private byte[] hDH;
  59. private byte[] K_S;
  60. private byte[] sigH;
  61. private KeyPair dsa;
  62. private String kex_alg = "diffie-hellman-group1-sha1";
  63. private String server_alg = "ssh-dss";
  64. private String encrypt_alg_c = "aes128-ctr";
  65. private String encrypt_alg_s = "aes128-ctr";
  66. private String mac_alg_c = "hmac-sha1";
  67. private String mac_alg_s = "hmac-sha1";
  68. private String comp_alg_c = "none";
  69. private String comp_alg_s = "none";
  70. private int cipherBlockSize = 16;
  71. private STATE state = STATE.NONE;
  72. @Override
  73. public int getPort() {
  74. return 22;
  75. }
  76. @Override
  77. public TALK_FIRST whoTalksFirst() {
  78. return TALK_FIRST.SERVER;
  79. }
  80. @Override
  81. public List<ByteArray> processMessage(ByteArray message) {
  82. List<ByteArray> response = new ArrayList<ByteArray>();
  83. byte[] request = null;
  84. if(message != null) request = message.get();
  85. switch(connectionState) {
  86. case NONE:
  87. response.add(new ByteArray(serverVersion + serverType + "\r\n"));
  88. response.add(new ByteArray(kexInit()));
  89. connectionState = STATE.SRVR_VERSION;
  90. break;
  91. case SRVR_VERSION:
  92. if(request != null && request.length >= 8) {
  93. extractType(request);
  94. connectionState = STATE.CLNT_VERSION;
  95. }
  96. break;
  97. case CLNT_VERSION:
  98. if(request != null && request.length > 5 && request[5] == 0x14) {
  99. extractCookie(request);
  100. connectionState = STATE.KEX_INIT;
  101. }
  102. break;
  103. case KEX_INIT:
  104. if(request.length > 5 && request[5] == 0x1e) {
  105. extractPubKey(request);
  106. response.add(new ByteArray(dhKexReply()));
  107. connectionState = STATE.DH_KEX_REP;
  108. }
  109. break;
  110. case DH_KEX_REP:
  111. connectionState = STATE.CLOSED;
  112. break;
  113. case CLOSED:
  114. break;
  115. default:
  116. connectionState = STATE.CLOSED;
  117. break;
  118. }
  119. return response;
  120. }
  121. @Override
  122. public boolean isClosed() {
  123. return (state == STATE.CLOSED);
  124. }
  125. @Override
  126. public boolean isSecure() {
  127. return false;
  128. }
  129. @Override
  130. public Class<ByteArray> getType() {
  131. return ByteArray.class;
  132. }
  133. @Override
  134. public String toString() {
  135. return "SSH";
  136. }
  137. private byte[] wrapPckt(byte[] payload) {
  138. int pcktLen = 5 + payload.length; //4 byte packet length, 1 byte padding length, payload length
  139. int paddLenCBS = cipherBlockSize - (pcktLen % cipherBlockSize);
  140. int paddLen8 = 8 - (pcktLen % 8);
  141. int paddingLen = paddLenCBS > paddLen8 ? paddLenCBS : paddLen8;
  142. if(paddingLen < 4) paddingLen += cipherBlockSize;
  143. pcktLen = pcktLen + paddingLen - 4; //add padding string length to packet length
  144. byte[] pcktLength = ByteBuffer.allocate(4).putInt(pcktLen).array();
  145. byte[] paddingLength = {(byte) paddingLen};
  146. byte[] paddingString = new byte[paddingLen];
  147. for(int i = 0; i < paddingLen; i++) {
  148. paddingString[i] = 0x00;
  149. }
  150. return concat(pcktLength, paddingLength, payload, paddingString);
  151. }
  152. private byte[] kexInit() {
  153. byte[] msgCode = {0x14};
  154. I_S = randomBytes(16);
  155. byte[] kexLength = ByteBuffer.allocate(4).putInt(kex_alg.getBytes().length).array();
  156. byte[] serverLength = ByteBuffer.allocate(4).putInt(server_alg.getBytes().length).array();
  157. byte[] encrypt_c_Length = ByteBuffer.allocate(4).putInt(encrypt_alg_c.getBytes().length).array();
  158. byte[] encrypt_s_Length = ByteBuffer.allocate(4).putInt(encrypt_alg_s.getBytes().length).array();
  159. byte[] mac_c_Length = ByteBuffer.allocate(4).putInt(mac_alg_c.getBytes().length).array();
  160. byte[] mac_s_Length = ByteBuffer.allocate(4).putInt(mac_alg_s.getBytes().length).array();
  161. byte[] comp_c_Length = ByteBuffer.allocate(4).putInt(comp_alg_c.getBytes().length).array();
  162. byte[] comp_s_Length = ByteBuffer.allocate(4).putInt(comp_alg_s.getBytes().length).array();
  163. byte[] language_c_s = {0x00, 0x00, 0x00, 0x00};
  164. byte[] language_s_c = {0x00, 0x00, 0x00, 0x00};
  165. byte[] kexFirsPckt = {0x00};
  166. byte[] reserved = {0x00, 0x00, 0x00, 0x00};
  167. byte[] response = concat(msgCode, I_S, kexLength, kex_alg.getBytes(), serverLength, server_alg.getBytes(),
  168. encrypt_c_Length, encrypt_alg_c.getBytes(), encrypt_s_Length, encrypt_alg_s.getBytes(), mac_c_Length, mac_alg_c.getBytes(),
  169. mac_s_Length, mac_alg_s.getBytes(), comp_c_Length, comp_alg_c.getBytes(), comp_s_Length, comp_alg_s.getBytes(),
  170. language_c_s, language_s_c, kexFirsPckt, reserved);
  171. return wrapPckt(response);
  172. }
  173. private byte[] dhKexReply() {
  174. generateDHKeys();
  175. generateHostKey();
  176. generateSha1Hash();
  177. generateSignature();
  178. byte[] msgCode = {0x1f};
  179. byte[] hostKeyLength = ByteBuffer.allocate(4).putInt(K_S.length).array();
  180. byte[] fDHLength = ByteBuffer.allocate(4).putInt(fDH.length).array();
  181. byte[] signatureLength = ByteBuffer.allocate(4).putInt(sigH.length).array();
  182. byte[] server_algLength = ByteBuffer.allocate(4).putInt(server_alg.getBytes().length).array();
  183. byte[] payloadLength = ByteBuffer.allocate(4).putInt(server_algLength.length + signatureLength.length + sigH.length + server_alg.getBytes().length).array();
  184. byte[] response = concat(msgCode, hostKeyLength, K_S,
  185. fDHLength, fDH, payloadLength, server_algLength, server_alg.getBytes(), signatureLength, sigH);
  186. return wrapPckt(response);
  187. }
  188. private byte[] newKeys() {
  189. byte[] msgCode = {0x15};
  190. return wrapPckt(msgCode);
  191. }
  192. private void generateDHKeys() {
  193. try {
  194. KeyPairGenerator myKpairGen = KeyPairGenerator.getInstance("DH");
  195. KeyAgreement myKeyAgree = KeyAgreement.getInstance("DH");
  196. BigInteger p = new BigInteger(pDH);
  197. BigInteger g = new BigInteger(gDH);
  198. BigInteger e = new BigInteger(eDH);
  199. DHParameterSpec dhParamSpec = new DHParameterSpec(p, g);
  200. myKpairGen.initialize(dhParamSpec);
  201. KeyPair myKpair = myKpairGen.generateKeyPair();
  202. myKeyAgree.init(myKpair.getPrivate());
  203. BigInteger f = ((DHPublicKey) (myKpair.getPublic())).getY();
  204. fDH = f.toByteArray();
  205. KeyFactory myKeyFac = KeyFactory.getInstance("DH");
  206. DHPublicKeySpec keySpec = new DHPublicKeySpec(e, p, g);
  207. PublicKey yourPubKey = myKeyFac.generatePublic(keySpec);
  208. myKeyAgree.doPhase(yourPubKey, true);
  209. byte[] mySharedSecret = myKeyAgree.generateSecret();
  210. kDH = mySharedSecret;
  211. } catch (Exception e) {
  212. e.printStackTrace();
  213. }
  214. }
  215. private void generateHostKey() {
  216. try {
  217. KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA");
  218. dsa = generator.generateKeyPair();
  219. byte[] string = "ssh-dss".getBytes();
  220. byte[] stringLength = ByteBuffer.allocate(4).putInt(string.length).array();
  221. byte[] p = ((DSAPublicKey) dsa.getPublic()).getParams().getP().toByteArray();
  222. if(p[0] != 0x00) p = concat(new byte[]{0x00}, p);
  223. byte[] pLength = ByteBuffer.allocate(4).putInt(p.length).array();
  224. byte[] q = ((DSAPublicKey) dsa.getPublic()).getParams().getQ().toByteArray();
  225. if(q[0] != 0x00) q = concat(new byte[]{0x00}, q);
  226. byte[] qLength = ByteBuffer.allocate(4).putInt(q.length).array();
  227. byte[] g = ((DSAPublicKey) dsa.getPublic()).getParams().getG().toByteArray();
  228. if(g[0] != 0x00) g = concat(new byte[]{0x00}, g);
  229. byte[] gLength = ByteBuffer.allocate(4).putInt(g.length).array();
  230. byte[] y = ((DSAPublicKey) dsa.getPublic()).getY().toByteArray();
  231. if(y[0] != 0x00) y = concat(new byte[]{0x00}, y);
  232. byte[] yLength = ByteBuffer.allocate(4).putInt(y.length).array();
  233. K_S = concat(stringLength, string, pLength, p, qLength, q, gLength, g, yLength, y);
  234. } catch (Exception e) {
  235. e.printStackTrace();
  236. }
  237. }
  238. private void generateSha1Hash() {
  239. try {
  240. MessageDigest sha = MessageDigest.getInstance("SHA-1");
  241. sha.update(V_C);
  242. sha.update(V_S);
  243. sha.update(I_C);
  244. sha.update(I_S);
  245. sha.update(K_S);
  246. sha.update(eDH);
  247. sha.update(fDH);
  248. sha.update(kDH);
  249. hDH = sha.digest();
  250. } catch (Exception e) {
  251. e.printStackTrace();
  252. }
  253. }
  254. private void generateSignature() {
  255. try {
  256. Signature sig = Signature.getInstance("SHA1withDSA");
  257. sig.initVerify(dsa.getPublic());
  258. sig.initSign(dsa.getPrivate());
  259. sig.update(hDH);
  260. sigH = doStuff(sig.sign());
  261. } catch (Exception e) {
  262. e.printStackTrace();
  263. }
  264. }
  265. private void extractType(byte[] request) {
  266. V_C = new byte[request.length - 10];
  267. for(int i = 0; i < V_C.length; i++) {
  268. if(request[i] == 0x0d) break;
  269. V_C[i] = request[i+8];
  270. }
  271. }
  272. private void extractCookie(byte[] request) {
  273. I_C = new byte[16];
  274. for(int i = 0; i < I_C.length; i++) {
  275. I_C[i] = request[i+6];
  276. }
  277. }
  278. private void extractPubKey(byte[] request) {
  279. eDH = new byte[byteToInt(new byte[] {request[6], request[7], request[8], request[9]})];
  280. for(int i = 0; i < eDH.length; i++) {
  281. eDH[i] = request[i+10];
  282. }
  283. }
  284. private static int byteToInt(byte[] bytes) {
  285. int ret = 0;
  286. for (int i=0; i < bytes.length; i++) {
  287. ret <<= 8;
  288. ret |= (int)bytes[i] & 0xFF;
  289. }
  290. return ret;
  291. }
  292. private byte[] randomBytes(int size) {
  293. byte[] bytes = new byte[size];
  294. Random rdm = new Random();
  295. rdm.nextBytes(bytes);
  296. return bytes;
  297. }
  298. private byte[] concat(byte[]...bytes) {
  299. int newSize = 0;
  300. for(byte[] b: bytes) newSize += b.length; //get total new size
  301. byte[] dst = new byte[newSize]; //create new array with new size
  302. int currentPos = 0;
  303. int newPos;
  304. for(byte[] b:bytes) { //for each elem b out of bytes
  305. newPos = b.length; //get b.length and new position
  306. System.arraycopy(b, 0, dst, currentPos, newPos); //copy b in dst from currentPos to newPos
  307. currentPos += newPos; //increase currentPos to newPos + currentPos
  308. }
  309. return dst;
  310. }
  311. private byte[] doStuff(byte[] sig) {
  312. // sig is in ASN.1
  313. // SEQUENCE::={ r INTEGER, s INTEGER }
  314. int len = 0;
  315. int index = 3;
  316. len = sig[index++] & 0xff;
  317. byte[] r = new byte[len];
  318. System.arraycopy(sig, index, r, 0, r.length);
  319. index = index + len + 1;
  320. len = sig[index++] & 0xff;
  321. byte[] s = new byte[len];
  322. System.arraycopy(sig, index, s, 0, s.length);
  323. byte[] result = new byte[40];
  324. // result must be 40 bytes, but length of r and s may not be 20 bytes
  325. System.arraycopy(r,
  326. (r.length > 20) ? 1 : 0,
  327. result,
  328. (r.length > 20) ? 0 : 20 - r.length,
  329. (r.length > 20) ? 20 : r.length);
  330. System.arraycopy(s,
  331. (s.length > 20) ? 1 : 0,
  332. result,
  333. (s.length > 20) ? 20 : 40 - s.length,
  334. (s.length > 20) ? 20 : s.length);
  335. return result;
  336. }
  337. }