2014/07/21

Just a Note:Jetty Runner

說明:
Jetty/Howto/Using Jetty Runner

下載:
Jetty Runner Download

看起來只有 for Jetty 7.x 和 8.x 的版本,不過應該已經足夠。

基本的需求是:
1. 把 Web Application 當作 stand alone 的應用程式部署。
2. 檔案愈少愈好。

如此便只需要 jetty runner 的 jar 檔和 application 的 war 檔共兩個檔案,透過 command line 便可以啟動 application,如此對 Application Server 的依賴度就會降低。

啟動程式:

java -jar jetty-runner.jar --path /myapp myapp.war

2014/07/17

Why Do I Choose Redmine for Issue Tracking?

必須先說明,工作以來用過 Mantis、Clear Case、Trac、Redmine 等 Issue Tracking System,Clear Case 要商用軟體就不用談了,真正有實際架設經驗的只有 Trac 和 Redmine,會選用 Redmine 作為主要 Issue Tracking 系統的原因如下,主要還是跟 Trac 進行比較:

1. 原生支援整合 Subversion、Git。
 這段分析對 Trac 比較不公平一些,之前使用 Trac 時,經歷了整合 Subversion 1.4.x、1.5.x、1.6.x 和 Git,每一階段都很痛苦,因為 python 的版本、Trac 版本、相關 plugin 版本的配合著實令人頭痛。後來改用 Redmine 時,Subversion Server 已經固定使用 1.6.x 版,所以只要選用支援的版本,基本上就可以無痛整合;另外隨著 Git 的普及,原生整合 Git 對我來說也是相當重要的功能。我之前同時嘗試用 Trac 和 Redmine 整合 Git,但最後 Trac 沒有試成功,而 Redmine 則是無痛整合。

2. 較完整的使用者/權限管理/設定,使用者亦可自行進行密碼變更。
 之前用 Trac,沒有使用者自行管理密碼的功能(可能需要額外安裝 plugin),使用者的密碼都是管理者設定後便固定不變,其實這樣有點怪。

3. 較完整的專案管理/設定
 原生支援 multiple projects、子專案,透過 Web 管理介面便可進行專案的建立與設定。Trac 則是需要先建立一個 template,每要開一個新的專案,就需要複製實體目錄,再手動進行 config 的修改,操作門檻較高,且基本上 Trac 各專案均為獨立個體,只能透過 Web Server 設定,「假裝」有一個統一的入口。

4. 原生支援 wiki/論壇/行事曆功能
 Trac 原生只有 wiki,論壇、行事曆均需額外安裝 plugin。真的經歷過安裝 Trac plugin 的痛苦,便能體會原生支援的美好。

5. Email 通知功能
 Redmine 原生支援。Trac 大概也是得安裝 plugin 吧......

6. 外部系統可整合 Redmine 的使用者,達到 Single Sign-On 的目的。
 目前已將 Subversion、Git、Jenkins 的登入統一由 Redmine 控管。



當然我的需求和考量的點不見得對每個專案與環境都適用,僅供大家參考。

網路上都說 Trac 架設的門檻高,根據經驗的確如此,當要使用其他 plugin 時,更是痛苦。不過嚴格說起來,其實 Redmine 的架設也不是真的很容易,要感謝 Bitnami 提供相當完整的 Installer,讓這些系統的架設變得更為容易。

2014/06/14

MOOC

最近開始接觸 MOOC(Massive Open Online Course,大規模網路免費公開課程),網路上有很多介紹的文章,我不敢說對每個人都會有顯著的效益,但這就跟在求學的時候一樣,每個人都上了很多課,但不見得獲得了相同的知識,所以還是蠻建議有興趣的人可以研究看看。
我目前只接觸了 Codecademy 和 Coursera,Codecademy 是屬於程式設計的課程,因為是本業,所以想借此看看現在的趨勢;Coursera 的領域則非常廣泛,感覺就很像大學的通識課。由於我只各參與了一門課(還沒修完),所以沒辦法進行評價,但可以分享一下初步接觸 Coursera 的感想。

1. 大多數都是英文課程,透過影片進行授課,影片可以開啟字幕、可以調整速度、可以隨時暫停,所以對於想提升英文能力的人,我相信一定有幫助。
2. 如果是想快速了解課程的內容,影片幾乎都有簡體中文、甚至正體中文的字幕,相信可以提升學習的效率。
3. 很多已經結束的課程都開放隨時選修,只是就不清楚是不是還有做業、考試的互動了。
4. 課程會區分成許多段最多數十分鐘的影片,每週時數加總起來大概也不會超過兩小時,所以如果加上練英聽、做作業,粗估每週花費 12 ~ 24 小時應該就可以完成該週的進度。
5. Android 和 iOS 上都有 Coursera 的 App,影片都可以下載到手機或平板上,可以利用零碎的時間反覆觀看。

附上一些介紹 MOOC 的連結,提供給大家做參考:
http://www.thenewslens.com/post/45399/
http://www.thenewslens.com/post/36571/
http://www.inside.com.tw/2014/06/13/a-taiwanese-developer-thought-about-coursera

2014/06/11

Just a Note:StandardPasswordEncoder 解析

StandardPasswordEncoder 是 Spring Security 提供的 class,僅有兩個 method:encode 和 matches,分別用來對密碼進行編碼及驗證。

StandardPasswordEncoder(CharSequence secret)
String encode(CharSequence rawPassword)
boolean matches(CharSequence rawPassword,String encodedPassword)

編碼的演算法基本上為 Hash,但並非單純的 Hash,其因子有三:
1. Secret:透過 Constructor 傳入,用來和 Raw Password 一起進行 Hash 的資料,通常為有意義的唯一值,例如帳號,以避免不同帳號、相同密碼產生出一模一樣的 Hash 值。
2. Raw Password:密碼明碼。
3. Salt:為一亂數,用來強化密碼編碼。

編碼的基本流程:
0. 使用者註冊時,輸入帳號、密碼。
1. 根據帳號 (Secret) new 出一個 StandardPasswordEncoder 物件。
2. 呼叫 encode,傳入密碼 (Raw Password)。
 (1) 產生 n byte 的 Salt。StandardPasswordEncoder 中會產生 8 bytes 的亂數。同一系統中,Salt 的長度需相同,除非連長度都當作產生 Hash 的因子保存下來。
 (2) 將 (1) 產生的 Salt、1. 傳入的 Secret、2. 傳入的 Raw Password 組合起來一起進行 Hash 運算,產生 Hash。StandardPasswordEncoder 採用 SHA-256 演算法,並針對資料進行 1024 次 Hash 運算。
 (3) 取得 Salt+ Hash (Encoded Password),存入資料庫。

步驟 2. (1) 加入了未知長度、且每次隨機產生的 Salt,即使使用相同帳號 (Secret)、相同密碼 (Raw Password),每次編碼出來的結果也不會相同;
步驟 2. (2) 進行了多次 Hash 演算,最後的結果就不會是以單純的 ASCII 字元產生出的 Hash,而是值域更廣 binary data,可以增加暴力破解的難度。

驗證的基本流程:
0. 使用者登入時,輸入帳號、密碼。
1. 根據帳號 (Secret) new 出一個 StandardPasswordEncoder 物件。
2. 呼叫 matches,傳入密碼 (Raw Password) 和從資料庫取得的 Encoded Password。
 (1) 從 Encoded Password 中取出 Salt 和 Hash。
 (2) 將 (1) 取得的 Salt、1. 傳入的 Secret、2. 傳入的 Raw Password 組合起來一起進行 Hash 運算,產生 Hash'。
 (3) 比對 (2) 產生的 Hash' 和 (1) 取得的 Hash 是否相等。相等則代表密碼驗證成功。


原則上透過 StandardPasswordEncoder 的 encode 出來的結果已經很難透過暴力破解取得使用者密碼了,不過也可以再進一步調整部分編碼的參數,例如:
1. Salt 的長度。
2. Hash 時 Salt、Secret、Raw Password 排列組合。
3. Hash 的演算法和次數。
4. Salt、Hash 的排列組合。
只要在 encode 和 matches 中使用相同的因子,便可以自訂出獨有的密碼編碼機制來強化密碼的保護。

如此一來,即便有內部人員可以直接存取資料庫,也無法取得使用者的密碼。

2014/06/05

詐騙簡訊...

友人分享了一個詐騙簡訊,簡訊說「這是上次聚會的照片」,還附上了一個短網址。

短網址還原後,是個韓國的 IP,話說上次也是韓國的 IP,不知道是不是這個空間比較好租......

網址是個 php,但是居然可以瀏覽目錄(也太不專業了)。
目錄底下有兩個 php、一個 txt、一個 jpg、一個 apk( 一.一+ )。

php 沒辦法下載,會自動 rediret 去下載 apk 檔;
jpg 其實是個 xuite 的網頁,是個聚會的照片,跟簡訊描述一致,所以其實不算詐騙?(是這樣解釋的嗎?)
txt 是 log 檔,記錄了所有點擊簡訊內連結的裝置資訊,例如手機型號、OS版本、瀏覽器版本、IP、點擊的時間等等。附帶一提,這個 log 檔有 203 MB,包含 5/21 到今天的點擊資訊,總共有 63 萬筆......也就是每天有 4 萬多次點擊,其中有非常多人是一而再、再而三地點、不停地點......

apk 是重頭戲啦!馬上拿去反組譯,看看到底寫了甚麼神奇的功能,可以達到詐騙的目的。
基本上是一個在 Android 上可以跟遠端溝通的常駐程式,主要功能如下:
1. 取得此裝置通訊錄。
2. 遠端可偵測此裝置是否上線。
3. 取得此裝置所有收到的簡訊。(重點!)
4. 上傳所有目前裝置內的簡訊。
5. 透過此裝置發簡訊。

所以當使用者點擊了詐騙簡訊的連結、也安裝了 apk 後,這時候小額付款機制就派上用場了......
詐騙集團使用安裝木馬的手機號碼進行小額付費,小額付費會發認證簡訊到該手機上,因為上述 3 的功能,詐騙集團可以立刻拿到授權碼(根本不需要用 line 跟你裝熟、請你幫忙收簡訊......),其實現實中還有很多使用簡訊 OTP 進行認證授權的機制,這時候都變成詐騙集團可以利用的管道。
然後你的荷包就被掏空了......

報告完畢,謝謝收看。

2014/04/13

Android App:Daily Money New Branch for DB Schema Compatibility

This project is a fork of original Daily Money project hosted on github by Dennis Chen

https://github.com/dennischen/daily-money

建立新的分支,在不改變官方資料結構的情況下加入新功能。
所以此分支不包含
1. 金額由 double 轉 BigDecimal 的處理。
2. Tag 功能。

APK can be downloaded in dist folder.


完整功能版本:
Source & Binary:
https://github.com/LancelotLiu/daily-money-1/tree/lancelot
APK can be downloaded in dist folder.

2014/03/07

Just a Note:Sun JCE 驗章問題

驗章時須使用 Public Key,一般來說,在 JCE 下使用已知的 Key 值建立 Public Key 的方式有兩種:

X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);

差別在於 Key 值的保存型態,
第一種存的是 Key.getEncoded() 的值 (http://docs.oracle.com/javase/7/docs/api/java/security/Key.html),
第二種保存的是 Public Key 的 modulus 和 exponent。而使用第二種方式時,通常我們亦會把 modulus 和 exponent 用 byte[] 保存。

最近遇到的問題是,
如果 Public Key 是透過 RSAPublicKeySpec 建立的話,會驗章失敗;使用 X509EncodedKeySpec 建立的話,則會成功。
但是把兩種方式建出來的 Public Key dump 出來 (getEncoded()),卻會得到一模一樣的值。

Debug 後發現,
RSAPublicKeySpec 建出來的 Key,其 modulus 是負數,
X509EncodedKeySpec 建出來的 Key,其 modulus 則是正數。

解決方式:
當 modulus 的第一個 byte 大於等於 0x80 時,需在最前面多放一個 byte 0x00,再轉成 BigInteger,即能正確取得 modulus 的值。

String modulusStr = "C868EEE8D1D300207EA65DB1DBBFD52552AE1814E07FDF23DE040EC96E6F0F35C8ECF922C11D7F39B3E3162B96881447D16B0FCC75FB0378F2B19AFB22884B8F2E2840C74FE73DBCDBF653E26070955BB9A4011C1EDD9E1DBA482F02091DBBFAD77D13F91A06FF76C8FDE1E4F475A2DFF8A9567C115F50ACCA573315F7723D333218E266984953D0642057C46D6F952CFC7CFAEAA8945FAEDE441ADA8497F0895C1AC18CE6679467F6EFEF5C741879487E94B8DAF87FA45E87FD011BB0A794B4F433B655959B6D1D8164793421A360A39E17A2287EE5CFE7200FF6FA3F407DD7FEC2EAA6C6A89BE5A3CE3073B758E56654C17CE17A3657E76E219E1BECA9115D";
byte[] modulusTmp = Util.hexStringToByte(modulusStr);
byte[] modulus = null;
if (modulusTmp[0] < 0) {
    modulus = new byte[modulusTmp.length + 1];
    modulus[0] = 0x00;
    System.arraycopy(modulusTmp, 0, modulus, 1, modulusTmp.length);
} else {
    modulus = new byte[modulusTmp.length];
    System.arraycopy(modulusTmp, 0, modulus, 0, modulusTmp.length);
}