2009年2月14日 星期六

Java Server 以AES加密資料透過 socket 傳輸到 Flex3 client 範例

這個範例是以 J2SE撰寫Server , 讓Flex3客戶端以socket連接到Server然後Server的資料以AES 128bit ECB PKCS5Padding加密傳輸到Flex3 , Flex3使用 com.hurlant.crypto.Crypto 套件解密 AES。

使用的 IDE工具為
Java : NetBeans IDE 6.5 + JDK1.6(J2SE)
Flex3 : AdobeFlex Builder 3 + Hurlant加解密套件

Source code 未經整理 , 只是純心得記錄!

Java Server 部分的 code: Server.java (要把 hurlant套件放在同一個 src 目錄下)
package servertest;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class Server extends ServerSocket {

private static ArrayList User_List = new ArrayList();
private static ArrayList Threader = new ArrayList();
private static int Thread_Counter = 0;
protected static final int SERVER_PORT = 8108;
DateFormat fDT = new SimpleDateFormat("yyyy/M/d H:m:s.S");
public static byte[] ciphertext;

public static void main(String[] args) throws IOException {

byte[] myKey = "1234567887654321".getBytes();
byte[] bText = "Kirk".getBytes();


byte[] decrypted;

SecretKeySpec key = new SecretKeySpec(myKey, "AES");

try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
ciphertext = cipher.doFinal(bText);
System.out.println(new String(ciphertext));
cipher.init(Cipher.DECRYPT_MODE, key);
decrypted = cipher.doFinal(ciphertext);
System.out.println(new String(decrypted));
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeyException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}

new Server();
}

public Server() throws FileNotFoundException, IOException {
super(SERVER_PORT);
new Broadcast();

try {
while (true) {
Socket socket = accept();
new CreateServerThread(socket);
}
} finally {
close();
}
}

class Broadcast extends Thread {

private byte[] EncryptS(String S) {
byte[] ciphertext = null;
byte[] myKey = "1234567887654321".getBytes();
try {
SecretKeySpec key = new SecretKeySpec(myKey, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
ciphertext = cipher.doFinal(S.getBytes());
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeyException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
return ciphertext;
}

public Broadcast() {
start();
}

@Override
public void run() {
while (true) {
Date Now = new Date();
for (int i = 0; i < Threader.size(); i++) {
CreateServerThread client = (CreateServerThread) Threader.get(i);
client.sendMessage(EncryptS("0300," + fDT.format(Now)));
//client.sendMessage(Server.ciphertext);
}
try {
Thread.sleep(800);
} catch (InterruptedException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}

class RecvThread extends Thread {

private Socket client;
private BufferedReader in;

public RecvThread(Socket s) throws IOException {
client = s;
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
start();
}

@Override
public void run() {
String s = "";
while (!client.isClosed()) {
try {
if ((s = in.readLine()) != null) {
System.out.println(s);
} else if (s == null) {
client.close();
}
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}


//--- CreateServerThread
class CreateServerThread extends Thread {

private Socket client;
private BufferedReader in;
private DataOutputStream out;
private String Username;
private LinkedList MSGs = new LinkedList();
private Date lastResponse = new Date();

public CreateServerThread(Socket s) throws IOException {
client = s;
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
//out = new PrintWriter(client.getOutputStream(), true);
out = new DataOutputStream(client.getOutputStream());
new RecvThread(client);
start();
}

public void sendMessage(byte[] msg) {
System.out.println(msg);
if (msg != null) {
MSGs.add(msg);
}
}

@Override
public void run() {
try {
Thread_Counter++;
Threader.add(this);

String s;
while (!client.isClosed()) {
if (MSGs.size() > 0) {
//String tmm = (String) MSGs.getFirst();
//MSGs.removeFirst();
byte[] tmn = (byte[]) MSGs.getFirst();
out.write(tmn, 0, tmn.length);
//out.println(tmn);
MSGs.removeFirst();
}
Thread.sleep(20);
}
client.close();
} catch (InterruptedException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
client.close();
} catch (IOException e) {
}
Thread_Counter--;
Threader.remove(this);
User_List.remove(Username);
}
}
}
}

Flex的source code如下: EncReceiver.as
package
{
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.util.Hex;

import flash.display.Shape;
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.external.ExternalInterface;
import flash.utils.ByteArray;
import flash.utils.Timer;

public class EncReceiver extends Sprite
{
private var socket:CustomSocket;
private var tt:Timer;
private var tt1:Timer;
private var CGUID:String="";
private var BBHOST:String="";
private var BBPORT:String="";
private var FIN:String="";
private var isConnecting:Boolean=false;
private var circle:Shape;
private var loc:Boolean=false;

private var key:ByteArray;
private var cipher:ICipher;

private var data:ByteArray;
private var EncString:String="";

private var dcounter:int=0;

public function EncReceiver()
{
ExternalInterface.addCallback("ENCODE", onENCODE);
ExternalInterface.addCallback("DECODE", onDECODE);

key=Hex.toArray("b6e39f9ee5f571ba9496f068c9d390ad");
cipher=Crypto.getCipher("aes128-ecb", key);

circle=new Shape();
circle.graphics.beginFill(0xff0000, 0.5);
circle.graphics.drawCircle(10, 10, 10);
circle.graphics.endFill();
addChild(circle);


CGUID=root.loaderInfo.parameters["CGUID"];
//BBHOST = root.loaderInfo.parameters["BBHOST"];
//BBPORT = root.loaderInfo.parameters["BBPORT"];
BBHOST="ap1.spp888.com";
BBPORT="7070";

BBHOST="192.168.88.140";
BBPORT="8108";

socket = new CustomSocket(BBHOST,int(BBPORT));

tt=new Timer(500, 0);
tt.addEventListener(TimerEvent.TIMER, onTT);
tt.start();
tt1=new Timer(100, 0);
tt1.addEventListener(TimerEvent.TIMER, onTT1);
tt1.start();
}

private function onENCODE():void
{
EncString="Hello 我是帥哥... 這是 AES128bits 加密系統... 你好嘛??";
data=Hex.toArray(Hex.fromString(EncString));
cipher.encrypt(data);
ExternalInterface.call("setFOUT", Hex.fromArray(data));
}


private function onDECODE():void
{
cipher.decrypt(data);
ExternalInterface.call("setFOUT", Hex.toString(Hex.fromArray(data)));
}


private function onTT1(event:TimerEvent):void
{
if (socket)
if (socket.connected)
{
circle.graphics.clear();
circle.graphics.beginFill(0x00ff00, 0.8);
if (loc)
{
circle.graphics.drawCircle(20, 10, 10);
}
else
{
circle.graphics.drawCircle(10, 10, 10);
}
circle.graphics.endFill();
loc=!loc;
dcounter=0;
}
else
{
circle.graphics.clear();
circle.graphics.beginFill(0xff0000, 0.8);
if (loc)
{
circle.graphics.drawCircle(20, 10, 10);
}
else
{
circle.graphics.drawCircle(10, 10, 10);
}
circle.graphics.endFill();
dcounter++;
//ExternalInterface.call("setFOUT", "disconnected : " + dcounter);
isConnecting=false;
tt.start(); //try connect
}

}

private function onTT(event:TimerEvent):void
{
if (!isConnecting)
{
//ExternalInterface.call("showalert", "connect");
//socket=new CustomSocket(BBHOST, int(BBPORT));
socket.getconnect();

isConnecting=true;
}

if (socket.connected)
{
tt.stop();
trace("timer stop!");
//loginto();
}
}

private function loginto():void
{
socket.writeln("100"); //獲得全部期貨商品資料
socket.writeln("109"); //獲得全部期貨商品資料
}

}
}

import flash.errors.*;
import flash.events.*;
import flash.net.Socket;
import mx.utils.StringUtil;
import flash.external.ExternalInterface;
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.util.Hex;
import flash.utils.ByteArray;

class CustomSocket extends Socket
{

public var response:String="";
public var FOUT:String="";
public var FOUTINDEX:String="";

private var key:ByteArray;
private var cipher:ICipher;

private var data:ByteArray;
private var EncString:String="";

private var bbhost:String="";
private var bbport:int=0;


public function CustomSocket(host:String=null, port:uint=0)
{
bbhost = host;
bbport = port;
super();
configureListeners();
}

public function getconnect():void {
super.connect(bbhost, bbport);
}

private function configureListeners():void
{
addEventListener(Event.CLOSE, closeHandler);
addEventListener(Event.CONNECT, connectHandler);
addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
}

public function writeln(str:String):void
{
str+="\r\n";
try
{
writeUTFBytes(str);
flush();
}
catch(e:IOError)
{
trace(e);
}
}

private function readResponse():void
{
var bytes:ByteArray=new ByteArray();
readBytes(bytes, 0, bytesAvailable);

//var str:String = readUTFBytes(bytesAvailable);
//response = str;
//trace("resp:" + response);
//用 0XOA 0XOD 切 TOKEN

//var bb:Array = str.split("\r\n");

key=Hex.toArray(Hex.fromString("1234567887654321"));
cipher=Crypto.getCipher("aes128-ecb", key);


//for (var i:int=0;i<bb.length-1;i++) {
//var sstr:String = bb[i];
data=bytes;
cipher.decrypt(data);
ExternalInterface.call("setFOUT", Hex.toString(Hex.fromArray(data)));
//}


}

private function closeHandler(event:Event):void
{
trace("closeHandler: " + event);
trace(response.toString());
}

private function connectHandler(event:Event):void
{
trace("connectHandler: " + event);
//sendRequest();
}

private function ioErrorHandler(event:IOErrorEvent):void
{
trace("ioErrorHandler: " + event);
}

private function securityErrorHandler(event:SecurityErrorEvent):void
{
trace("securityErrorHandler: " + event);
}

private function socketDataHandler(event:ProgressEvent):void
{
trace("socketDataHandler: " + event);
readResponse();
}
}

Flex3 跑的 html code 如下 : EncReceiver.html (EncReceivere.swf要放在同一個目錄)
<!-- saved from url=(0014)about:internet -->
<html lang="en">

<!--
Smart developers always View Source.

This application was built using Adobe Flex, an open source framework
for building rich Internet applications that get delivered via the
Flash Player or to desktops via Adobe AIR.

Learn more about Flex at http://flex.org
// -->

<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5" />

<!-- BEGIN Browser History required section -->
<link rel="stylesheet" type="text/css" href="history/history.css" />
<!-- END Browser History required section -->

<title></title>
<script src="AC_OETags.js" language="javascript"></script>

<!-- BEGIN Browser History required section -->
<script src="history/history.js" language="javascript"></script>
<!-- END Browser History required section -->

<style>
body { margin: 0px; overflow:hidden }
</style>
<script language="JavaScript" type="text/javascript">
<!--
// -----------------------------------------------------------------------------
// Globals
// Major version of Flash required
var requiredMajorVersion = 9;
// Minor version of Flash required
var requiredMinorVersion = 0;
// Minor version of Flash required
var requiredRevision = 124;
// -----------------------------------------------------------------------------
// -->

var flashplayer;

function detectFP() {
if (navigator.appName.indexOf("Microsoft") != -1) {
flashplayer = window.EncReceiver;
} else {
//alert("no IE");
flashplayer = window.document.EncReceiver;
}
}


function showalert(ttt) {
alert(ttt);
}

function getPARAM(ttt) {
return document.getElementById(ttt).value;
}

function setFOUT(ttt) {
document.getElementById("fooo").innerHTML = ttt;
}


</script>
</head>

<body onload="detectFP();">
<script language="JavaScript" type="text/javascript">
<!--
// Version check for the Flash Player that has the ability to start Player Product Install (6.0r65)
var hasProductInstall = DetectFlashVer(6, 0, 65);

// Version check based upon the values defined in globals
var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);

if ( hasProductInstall && !hasRequestedVersion ) {
// DO NOT MODIFY THE FOLLOWING FOUR LINES
// Location visited after installation is complete if installation is required
var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
var MMredirectURL = window.location;
document.title = document.title.slice(0, 47) + " - Flash Player Installation";
var MMdoctitle = document.title;

AC_FL_RunContent(
"src", "playerProductInstall",
"FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
"width", "35",
"height", "25",
"align", "middle",
"id", "EncReceiver",
"quality", "high",
"bgcolor", "#ffffff",
"name", "EncReceiver",
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else if (hasRequestedVersion) {
// if we've detected an acceptable version
// embed the Flash Content SWF when all tests are passed
AC_FL_RunContent(
"src", "EncReceiver",
"width", "35",
"height", "25",
"align", "middle",
"id", "EncReceiver",
"quality", "high",
"bgcolor", "#ffffff",
"name", "EncReceiver",
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else { // flash is too old or we can't detect the plugin
var alternateContent = 'Alternate HTML content should be placed here. '
+ 'This content requires the Adobe Flash Player. '
+ '<a href=http://www.adobe.com/go/getflash/>Get Flash</a>';
document.write(alternateContent); // insert non-flash content
}


// -->
</script>
<noscript>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="EncReceiver"
id="EncReceiver" width="35" height="25"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="EncReceiver.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<param name="allowScriptAccess" value="sameDomain" />
<param name="FlashVars" value="CGUID={CGUID}&BBHOST=ap1.stw888.com&BBPORT=8009" />
</object>
</noscript>
<p>
<input type=button name=aaa value="Encode" onclick="flashplayer.ENCODE();">
<input type=button name=aaa value="Decode" onclick="flashplayer.DECODE();"><br>
<span id=fooo></span>
</body>
</html>


執行的時候先把 Java Server 跑起來

然後再跑 flex , 這時 IE 會被帶起來 , 成功執行後 , 會再 IE 下面顯示 0300,現在時間 , 如果時間有一直更新則為正確執行

這個 Flex 有斷線自動重連的機制

如果非本機執行 , 則要考慮 FlashPolicy 的問題 (Java Server 可以把 policy xml 加到連線送的資料上去)

沒有留言:

張貼留言

追蹤者