package com.owwlo.courier.s.poster; import android.content.ContentValues; import android.content.Context; import android.os.Message; import android.util.Base64; import android.util.Log; import com.owwlo.courier.db.CourierDatabaseHelper.ERROR_DETECT; import com.owwlo.courier.s.Constants; import com.owwlo.courier.s.Constants.SMS; import com.owwlo.courier.s.data.Contact; import com.owwlo.courier.s.data.MessageItem; import com.owwlo.courier.s.data.MessagePack; import com.owwlo.courier.s.utils.Utils; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class SocketPoster extends Poster { private static final String TAG = "CourierSSocketPoster"; private Map<Socket, ClientAnswerThread> mClientThreads; private Context mContext; private ServerSocket mServerSocket; private MessagePosterManager mMessagePosterManager = MessagePosterManager .getInstance(); public SocketPoster(Context paramContext) { mContext = paramContext; mClientThreads = Collections .synchronizedMap(new HashMap<Socket, ClientAnswerThread>()); try { mServerSocket = new ServerSocket( mMessagePosterManager.getTcpListeningPort()); } catch (IOException localIOException) { Log.w(TAG, "SocketPoster create failed."); } } @Override public void init() { new Thread(this).start(); } @Override protected void finalize() throws Throwable { super.finalize(); mServerSocket.close(); } @Override public void run() { while (true) { try { Socket localSocket = mServerSocket.accept(); ClientAnswerThread localClientAnswerThread = new ClientAnswerThread( mContext, localSocket); localClientAnswerThread.addClientListener(new ClientListener() { @Override public void onClientConnecting() { } @Override public void onClientConnected(ClientAnswerThread cat) { ContentValues values = new ContentValues(); values.put(ERROR_DETECT.IP, cat.getSocket().getRemoteSocketAddress().toString()); values.put(ERROR_DETECT.CONNECT_TIME, System.currentTimeMillis()); mContext.getContentResolver().insert(Constants.URI_LAST_CONNECT, values); } @Override public void onClientDisconnected() { // TODO 从mClientThreads删除当前Client链接 checkIfLastExit(); } }); localClientAnswerThread.start(); mClientThreads.put(localSocket, localClientAnswerThread); Log.i(TAG, "ConnectIn " + localSocket.toString()); } catch (IOException localIOException) { Log.w(TAG, "Accept incomming connection Failed."); } } } private void checkIfLastExit() { if (mClientThreads.values().size() == 0) { notifyOnLastClientExit(); } } @Override public boolean sendMessage(Message paramMessage) { if ((mClientThreads == null) || (mClientThreads.size() == 0) || (mServerSocket == null)) { return false; } Iterator<ClientAnswerThread> localIterator = mClientThreads.values() .iterator(); while (localIterator.hasNext()) { ((ClientAnswerThread) localIterator.next()) .sendMessageToClient(paramMessage); } return true; } public static class ClientAnswerThread extends Thread { private final String TAG = "CourierSClientAnswerThread"; private static final int MAX_ENCRYPT_BLOCK = 117; private byte[] IV = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private IvParameterSpec ivSpec = new IvParameterSpec(IV); private Context mContext; private Socket mSocket; private boolean encryptEnabled = false; private PrintWriter mSender; private PublicKey mPublicKey; private SecretKey mAESKey; private List<ClientListener> mPosterListener; private ClientConnectionState mConnectionState; private enum ClientConnectionState { Connected, WaitingForPublicKey } public final Socket getSocket() { return mSocket; } public ClientAnswerThread(Context paramSocket, Socket arg3) { mContext = paramSocket; mSocket = arg3; mPosterListener = Collections .synchronizedList(new ArrayList<ClientListener>()); try { mSender = new PrintWriter(mSocket.getOutputStream()); } catch (IOException e) { Log.i(TAG, "failed to get printer from OutputStream."); e.printStackTrace(); } // 连接上后第一个初始状态是“已连接” mConnectionState = ClientConnectionState.Connected; /** * TODO 加密通讯需要调试通 */ // sendEncryptOption(); } private void sendEncryptOption() { JSONObject json = new JSONObject(); try { json.put(Constants.JSON_TYPE, Constants.JSON_TYPE_NEED_ENCRYPT); } catch (JSONException e) { e.printStackTrace(); } sendMessageToClient(json); // 连接后发送完加密选项后为“等待公钥”状态 mConnectionState = ClientConnectionState.WaitingForPublicKey; } private JSONObject getJSON(MessagePack pack) { JSONObject json = new JSONObject(); try { json.put(Constants.JSON_TYPE, Constants.JSON_MESSAGE_PACK); json.put(Constants.JSON_MSG_PACK_SENDER, pack.senderAddress); json.put(Constants.JSON_MSG_PACK_THREAD_ID, pack.senderThreadId); long personId = Utils.getContactInfoFormPhone(mContext, pack.senderAddress).mPersonId; json.put(Constants.JSON_MSG_PACK_SENDER_ID, personId); JSONArray msgArray = new JSONArray(); Map<Long, Contact> contactMap = new HashMap<Long, Contact>(); for (MessageItem item : pack.getMessageList()) { msgArray.put(getJSON(item)); contactMap.put(item.getContact().mPersonId, item.getContact()); } JSONObject imageJson = new JSONObject(); for (long pId : contactMap.keySet()) { imageJson.put(String.valueOf(pId), Utils .encodeImageTobase64(contactMap.get(pId).getImage( mContext))); } json.put(Constants.JSON_MSG_PACK_MSG_ARRAY, msgArray); json.put(Constants.JSON_MSG_PACK_IMG_ARRAY, imageJson); } catch (JSONException e1) { e1.printStackTrace(); } return json; } private JSONObject getJSON(MessageItem sms) { JSONObject json = new JSONObject(); try { json.put(Constants.JSON_TYPE, Constants.JSON_MESSAGE); json.put(SMS.ADDRESS, sms.getAddress()); json.put(SMS.BODY, sms.getBody()); json.put(SMS.DATE, sms.getDate()); json.put(SMS.DATE_SENT, sms.getDateSent()); json.put(SMS.DELETED, sms.getDeleted()); json.put(SMS.ID, sms.getId()); json.put(SMS.PROTOCOL, sms.getProtocol()); json.put(SMS.READ, sms.getRead()); json.put(SMS.SEEN, sms.getSeen()); json.put(SMS.STATUS, sms.getStatus()); json.put(SMS.THREAD_ID, sms.getThreadId()); json.put(SMS.TYPE, sms.getType()); json.put(SMS.PERSON, sms.getContact().mPersonId); if (sms.getUserImage() != null) { json.put(SMS.USER_IMAGE, Utils.encodeImageTobase64(sms.getUserImage())); } } catch (JSONException e) { // 不会执行到这里 e.printStackTrace(); } return json; } public void run() { for (ClientListener cl : mPosterListener) { cl.onClientConnected(this); } BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader( mSocket.getInputStream())); String l; while (true) { if ((l = br.readLine()) != null) { Log.i(TAG, "Receive String: " + l); processMessage(l); } } } catch (IOException e1) { e1.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { } } } } private void processMessage(String string) { try { string = new String(Base64.decode(string.getBytes("UTF-8"), Base64.NO_WRAP)); } catch (UnsupportedEncodingException e) { return; } if (!string.startsWith(Constants.COURIER_JSON_HEADER)) { return; } string = string.substring(Constants.COURIER_JSON_HEADER.length()); if (encryptEnabled) { try { // byte[] enCodeFormat = mAESKey.getEncoded(); // SecretKeySpec key = new SecretKeySpec(enCodeFormat, // "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, mAESKey); byte[] result = cipher.doFinal(string.getBytes("utf-8")); string = new String(result); } catch (NoSuchAlgorithmException e) { // 这里应该不会执行到 e.printStackTrace(); } catch (NoSuchPaddingException e) { // 这里应该不会执行到 e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { // 这里应该不会执行到 e.printStackTrace(); } catch (UnsupportedEncodingException e) { // 这里应该不会执行到 e.printStackTrace(); } } JSONObject json; try { json = new JSONObject(string); } catch (JSONException e) { e.printStackTrace(); Log.e(TAG, "failed to parse JSON from received data."); return; } try { jsonProcessor(json); } catch (JSONException e) { e.printStackTrace(); Log.e(TAG, "bad JSON format."); return; } catch (Exception e) { e.printStackTrace(); } } /* * 主要来处理并分发传来的消息功能的函数 */ private void jsonProcessor(JSONObject json) throws Exception { String type = json.getString(Constants.JSON_TYPE); if (mConnectionState == ClientConnectionState.Connected) { if (Constants.JSON_TYPE_REPLY_MSG.equalsIgnoreCase(type)) { MessageItem msg = new MessageItem(); msg.setAddress(json.getString(SMS.ADDRESS)); msg.setBody(json.getString(SMS.BODY)); Utils.sendMessage(mContext, msg); } } else if (mConnectionState == ClientConnectionState.WaitingForPublicKey) { if (Constants.JSON_TYPE_PUBLIC_KEY.equalsIgnoreCase(type)) { String publicKeyPEM = json.optString(Constants.JSON_VALUE); Log.d(TAG, "Got Public Key in PEM format: " + publicKeyPEM); String[] linesArray = publicKeyPEM.split("\n"); List<String> lines = new LinkedList<String>( Arrays.asList(linesArray)); if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size() - 1).startsWith("-----")) { lines.remove(0); lines.remove(lines.size() - 1); } else { throw new JSONException("Public Key format wrong."); } StringBuilder sb = new StringBuilder(); for (String aLine : lines) sb.append(aLine); String keyString = sb.toString(); Log.d("log", "keyString: " + keyString); byte[] keyBytes; try { keyBytes = Base64.decode(keyString.getBytes("utf-8"), Base64.NO_WRAP); X509EncodedKeySpec spec = new X509EncodedKeySpec( keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); mPublicKey = keyFactory.generatePublic(spec); mAESKey = generateAESKey(); if (mAESKey == null) { throw new Exception("AESKey generating failed."); } // 将AESKey发送给PC端 sendMessageToClient(getSecureKeyJSON()); // TODO 这里需要验证是否发送成功 // 重新置为连接状态 mConnectionState = ClientConnectionState.Connected; encryptEnabled = true; // 测试加密生效否 sendTestEncryptMessage(); } catch (UnsupportedEncodingException e) { // 这里应该不会执行到 e.printStackTrace(); } catch (NoSuchAlgorithmException e) { // 这里应该也不会执行到 e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); throw new JSONException("Public Key format wrong."); } } } } private void sendTestEncryptMessage() { JSONObject json = new JSONObject(); try { json.put("owwlo", "owwlo is owwlo"); } catch (JSONException e) { e.printStackTrace(); } sendMessageToClient(json); } private JSONObject getSecureKeyJSON() { JSONObject json = new JSONObject(); // byte[] aesKeyBytes = mAESKey.getEncoded(); byte[] aesKeyBytes = null; try { aesKeyBytes = "owwlo".getBytes("utf-8"); } catch (UnsupportedEncodingException e2) { // TODO Auto-generated catch block e2.printStackTrace(); } Log.d(TAG, "AES key: " + new String(Base64.encode(aesKeyBytes, Base64.NO_WRAP))); KeyFactory keyFactory; try { keyFactory = KeyFactory.getInstance("RSA"); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, mPublicKey); // int inputLen = aesKeyBytes.length; // ByteArrayOutputStream out = new ByteArrayOutputStream(); // int offSet = 0; // byte[] cache; // int i = 0; // // 对数据分段加密 // while (inputLen - offSet > 0) { // if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { // cache = cipher.doFinal(aesKeyBytes, offSet, // MAX_ENCRYPT_BLOCK); // } else { // cache = cipher.doFinal(aesKeyBytes, offSet, inputLen // - offSet); // } // out.write(cache, 0, cache.length); // i++; // offSet = i * MAX_ENCRYPT_BLOCK; // } // byte[] encryptedData = out.toByteArray(); // out.close(); byte[] encryptedData = cipher.doFinal(aesKeyBytes); try { json.put(Constants.JSON_TYPE, Constants.JSON_TYPE_AES_KEY); json.put( Constants.JSON_VALUE, new String(Base64.encode(encryptedData, Base64.NO_WRAP))); } catch (JSONException e) { // 不会执行到这里 e.printStackTrace(); } } catch (NoSuchAlgorithmException e1) { // 不会执行 e1.printStackTrace(); } catch (NoSuchPaddingException e1) { // 不会执行 e1.printStackTrace(); } catch (InvalidKeyException e1) { // 不会执行 e1.printStackTrace(); } catch (IllegalBlockSizeException e) { // 不会执行 e.printStackTrace(); } catch (BadPaddingException e) { // 不会执行 e.printStackTrace(); } return json; } private SecretKey generateAESKey() { KeyGenerator kg; try { kg = KeyGenerator.getInstance("AES"); kg.init(256); return kg.generateKey(); } catch (NoSuchAlgorithmException e) { // 不会执行到这里 e.printStackTrace(); } return null; } public void sendMessageToClient(Message paramMessage) { Log.i(TAG, "send Message to Client " + mSocket.toString()); if (paramMessage.obj instanceof MessageItem) { sendMessageToClient(getJSON((MessageItem) paramMessage.obj)); } else if (paramMessage.obj instanceof MessagePack) { sendMessageToClient(getJSON((MessagePack) paramMessage.obj)); } } public void sendMessageToClient(JSONObject json) { Log.d(TAG, "Going to send Json: " + json.toString()); byte[] bytesTobeSent = null; try { bytesTobeSent = json.toString().getBytes("utf-8"); } catch (UnsupportedEncodingException e1) { // 不会执行 } if (encryptEnabled) { // byte[] enCodeFormat = mAESKey.getEncoded(); // SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher; try { cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, mAESKey, ivSpec); bytesTobeSent = cipher.doFinal(bytesTobeSent); } catch (NoSuchAlgorithmException e) { // 不会执行到这里 e.printStackTrace(); } catch (NoSuchPaddingException e) { // 不会执行到这里 e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { // PKCS5Padding对齐方法防止这种情况产生 e.printStackTrace(); } catch (BadPaddingException e) { // 不会执行到这里 e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { // 不会执行到这里 e.printStackTrace(); } } try { byte[] headerBytes = Constants.COURIER_JSON_HEADER .getBytes("utf-8"); byte[] data = new byte[bytesTobeSent.length + headerBytes.length]; System.arraycopy(headerBytes, 0, data, 0, headerBytes.length); System.arraycopy(bytesTobeSent, 0, data, headerBytes.length, bytesTobeSent.length); String base64 = Base64.encodeToString(data, Base64.NO_WRAP); Log.i(TAG, "Base64 String: " + base64); mSender.println(base64); mSender.flush(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } @Override protected void finalize() throws Throwable { mSender.close(); for (ClientListener cl : mPosterListener) { cl.onClientDisconnected(); } super.finalize(); } public void addClientListener(ClientListener listener) { mPosterListener.add(listener); } public void removeClientListener(ClientListener listener) { mPosterListener.remove(listener); } } public static interface ClientListener { public void onClientConnecting(); public void onClientConnected(ClientAnswerThread cat); public void onClientDisconnected(); } @Override public int getConnectedCount() { return mClientThreads.values().size(); } }