2013年7月30日 星期二

程式Memo - JAVA AES加密及Silverlight AES解密

JAVA端:

byte[] iv = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

private String encrypt(String plaintext, String key){
        String ret ="";
        try{
            AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
       
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec spec = new SecretKeySpec( key.getBytes(), "AES");
            cipher.init( Cipher.ENCRYPT_MODE, spec, paramSpec);
           
            byte[] encryptData = cipher.doFinal(plaintext.getBytes());
            ret = ByteTransfer.toHex(encryptData);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

          return ret;
 }


Silverlight端:

private string decryptAES_Silverlight(string encryptedContent)
        {
            string AES_key = MY_KEY;
            byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
           
            string decryptedContent = "";
            byte[] b_encryptedContent = HexStrToByteArray(encryptedContent);

            AesManaged aes = new AesManaged();
            aes.BlockSize = 128;
            aes.KeySize = 128;
            aes.Padding = PaddingMode.PKCS7;

            aes.Key = UTF8Encoding.UTF8.GetBytes(AES_key);
            aes.IV = iv;

            ICryptoTransform decryptTrans = aes.CreateDecryptor();
            MemoryStream decryptStream = new MemoryStream();
            CryptoStream decryptor = new CryptoStream(decryptStream, decryptTrans, CryptoStreamMode.Write);

            decryptor.Write(b_encryptedContent, 0, b_encryptedContent.Length);
            decryptor.FlushFinalBlock();

            //解密後的byte[]
            byte[] decryptBytes = decryptStream.ToArray();
            decryptedContent = UTF8Encoding.UTF8.GetString(decryptBytes);
            return decryptedContent;
        }

Note:
1. 看起來JAVA的預設應該是使用Rijndael,不需要設定Initialization Vector;只是Silverlight端不支援Rijndael,而AES的另一個class - AESManaged卻必須設定IV,為了配合在Silverlight使用AESManaged類別,所以在JAVA加密端也要加入IV的設定。

2. Cipher.getInstance()內若不傳入參數,預設應該是AES/ECB/NoPadding;AES/CBC/PKCS5Padding的設定是為了配合Silverlight端,因為AESManaged只能用CBC mode;至於padding的部分,因為要加密的資料不一定會是16 bytes的倍數,padding就少不了了,JAVA端設定PKCS5,C#端相對的必須設定PKCS7,這兩個mode運作方式在此情況下是一樣的。

3. JAVA端的String.getBytes()若不指定Encoding,預設應該為UTF-8或ISO-8859_1(此處看來是UTF-8),在silverlight端必須將Key以及解碼結果的byte陣列也做UTF-8的編碼處理。

4. HexStrToByteArray、ByteTransfer.toHex()方法均為另外寫的十六進位陣列轉換。

2013年6月3日 星期一

程式Memo - C# HTTP POST

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(Url);

ASCIIEncoding encoding = new ASCIIEncoding();
String postData = "data";
byte[] data = encoding.getByte(postData);

webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = data.Length;

Stream reqStream = webRequest.GetRequestStream();
reqStream.Write(data, 0, data.Length);

HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

string responseStr = new StreamReader(response.GetResponseStream()).ReadToEnd();


2013年1月30日 星期三

系統Memo - Silverlight Player影片起始載入問題

為了這個DRM專案被Silverlight搞了好久,菊花都痛了。

自行開發的Silverlight player一直有個問題,就是載入影片"有時候"會卡住,player狀態就停在manifest_ready和media_open中間不上不下。其實困擾有一大半都來自這個"有時候"出現的錯誤,說一是一說二是二程式有錯就不給跑簡單多了,所以我們花了滿多時間在釐清這個問題的原因。

最後從wireshark抓封包以及IIS server log發現,卡住的時候,player不會再送出任何request去抓video / audio fragment,大約是在第六秒和第八秒,切換抓取不同bitrate的時候就出了問題。

What the....!!!!!! How the hell would this happen

所以在載入影片的時候不能切換bitrate? Fuck!!!  這沒有道理啊!!! (後來發現切換bitrate真的跟卡住無關,因為測試用的silverlight player載入影片的時候切bitrate一點他媽的問題也沒有!!)

呼,真是太氣憤了。

我花了一下午新轉了一部低bitrate的影片,讓它容易切換,然後在測試player上卻捅出另一個樓子= = 太過頻繁的切換似乎會讓認證的cookie塞不進去(這只是猜測,實際讓認證抓不到cookie的原因不明),然後我又針對認證裡cookie null的處理作修改之後回來正式環境測試,一試就中,卡住的瞬間有夠想屎!!
所以下午他媽的白忙,player卡住不送request跟cookie null而被認證踢掉根本是兩個問題。

而且照這個情況看來,不送request對silverlight player或是IIS認證來說都不算是個錯誤,單純只是類似thread卡住而便秘又不敢講而已。

既然測試player沒問題,那就把正式player的流程改成跟它一樣吧!

所以我們把正式player的AutoPlay改成true,然後就跟喝了通樂一樣一路順暢............

What the F!!!!!!!!!!

至於為什麼會這樣到現在還沒搞懂。

另外,在載入影片的時候,如果預先載入的30秒都是同一個bitrate(ex. 1200k),什麼問題都沒有,所有的request也都會順利出現在wireshark以及server log裡;但只要載入的時候去抓另一個bitrate的fragment(ex. 2000k),就只會出現抓那個bitrate的request,也就是說,如果第六秒去抓,那就只看到第六秒的request(有時候會有第八秒的)。
為什麼其他的request都消失了,原因一樣不曉得,說不定跟cache有關係?

Update:
(冷靜)

1. 消失的request的確跟cache有關。
2. AutoPlay改成true之後,在第八秒卡住的問題依然存在,雖然無法確定跟AutoPlay無關,但至少不是唯一的原因。又做了一些測試後,我們猜測或許跟解析度有關,因為嵌在網頁上的player大小是400x300(4:3),所以在載入同樣是4:3的1200K影片(640x480)時一切正常,直到bitrate切換16:9的2000K影片(960x540),就立刻爆炸。這也是為什麼一開始在測試player沒有出現過這問題的原因,測試player一直是全螢幕打滿的,不會有比例不符合的錯誤。
但是網頁上的player牽涉到版面設計,沒辦法為了技術問題改變大小,所以我們改成在一開始載入的階段(manifest_ready -> media_opened)限制player抓取影片的bitrate,載入完成後才恢復成原先的multi-bitrate。
問題總算是解決了。




2013年1月11日 星期五

系統Memo - IIS Root Module 設定

指掛在Default Web Site層級的Module,掛載方式如一般應用程式(application)層級,
將DLL檔放在bin資料夾裡,讓web.config去讀取即可。

要注意的是,在這個層級的module,會影響底下所有應用程式的module,
必須額外加上不讓底下應用程式繼承本身設定的宣告,如下:

web.config內容:


< ?xml version="1.0" encoding="UTF-8"? >
< configuration >
< system.web >
        < customErrors mode="Off"/ >
< /system.web >
< location path="." inheritInChildApplications="false" >
    < system.webServer >
        < modules >
            < add name="DomainRedirect" type="DomainRedirect.Redirect"/ >
        < /modules >
    < /system.webServer >
< /location >
< /configuration >





       


   
       
           
       
   




2013年1月1日 星期二

系統Memo - Cookie的使用

串流服務的http request分兩種,一種是manifest request,一種是chunk request;(manifest request: http://myserver.com/videopath/sample.ism/manifest?queryString,chunk request: /videopath/sample.ism?QualityLevels(128000)&Fragments(audio=460683900))

queryString只有在manifest request裡才會出現,也無法用程式加在接下來的chunk request上,所以為了辨別連線是屬於哪一個使用者,想到的最好解法就是使用cookie。

cookie的限制有以下幾點:
1. 無法即時改變cookie的值:cookie儲存在client端,在server端建立並設定值之後,由response帶過去client,下一次的request才會抓的到cookie;要改變其中的值或是刪除它,都必須由response通知client端,也就是cookie的變更一定存在request和response的時間差。

2. 瀏覽器的安全設定不相同:由於服務是由兩台不同domain的server提供,認證程式發給的cookie屬於第三方,這方面Chrome比較開放,並不禁止任何第三方cookie寫入,對認證沒有什麼影響;IE有限制第三方cookie,必須在cookie header中加入P3P設定 - HttpContext.Current.Response.AddHeader("P3P", "CP=\"CAO PSA OUR\""); Safari一樣有限制第三方cookie,且對於Domain的認定並不是看cookie內的設定來決定,而是直接看來源,這就很麻煩了,代表為了Safari一種瀏覽器,我必須將兩種服務放在同一個domain下。

3. 相同的request url不會更新cookie:也就是說,如果我要把cookie的值當作session用,我必須確定不同session的request url不會一樣,所攜帶的cookie值才會不一樣;經測試,相同的url無視於response的更新指令,而被瀏覽器視作cookie無須更新。

4. cookie允許設定string以外的值,但是request只取的到string的值

總而言之,cookie並不好用,
如果能夠像Wowza那樣將sessionid加在url上就不用被cookie搞了。


2012年10月17日 星期三

系統Memo - IHttpModule 中使用Session State

這個問題其實挺弔詭的,沒有什麼文件清楚的寫出來(該死的微軟)
要在IHttpModule中使用Session有非常多的限制:

1. 網路上眾說紛紜,有人說只在AcquireRequestState和PreRequestHandlerExecute兩個事件中,才有辦法使用Session,但也有人說http module根本沒辦法使用session,造成不少困擾。

2. 經測試,即使是在上述兩個事件中,HttpApplication.Context.Session依然為null,我的猜測是這樣的方法只適用於.aspx之類的http request,其餘一律行不通。(我發的http request是抓.png或是manifest這類的行為)

最後採用的解法是:
http://forums.asp.net/t/1098574.aspx/1

我看不太懂裡面針對handler物件繞來繞去的原因是什麼,只大概猜說是要自己宣告一個Handler類別,產生handler物件,然後用原本的Application.Context.Handler傳進來,藉此啟動SessionStateModule,讓SessionState成為avaliable狀態。

以下只擷取相關部分,RequestBegin之類的事件省略,
Code snippet:


public void Init(HttpApplication application)
{
    // following two event handler are both required
    application.PostAcquireRequestState += new EventHandler(OnPostAcquireRequestState);
    application.PostMapRequestHandler += new EventHandler(OnPostMapRequestHandler);
}


/**
* for the usage of SessionState, replace the original handler with ours, force SessionState to be valiable
**/
public void OnPostMapRequestHandler(Object sender, EventArgs e)
{
    HttpApplication application = (HttpApplication)sender;

    if (application.Context.Handler is IReadOnlySessionState || application.Context.Handler is IRequiresSessionState)
    {
        // no need to replace the original handler
        return;
    }

    // swap the original handler
    application.Context.Handler = new TempHandler(application.Context.Handler);
}


public void OnPostAcquireRequestState(Object sender, EventArgs e)
{
    HttpApplication application = (HttpApplication)sender;
    TempHandler handler = HttpContext.Current.Handler as TempHandler;

     if (handler != null)
     {
         HttpContext.Current.Handler = handler.myHandler;
     }

    // from this moment, SessionState is valiable
    HttpSessionState session = application.Context.Session;

}


/**
* A temp handler used to force the SessionStateModule to load session state
**/
public class TempHandler : IHttpHandler, IRequiresSessionState
{
    internal readonly IHttpHandler myHandler; // store original application context handler

    public TempHandler(IHttpHandler originalhttpHandler)
    {
        this.myHandler = originalhttpHandler;
    }

    public void ProcessRequest(HttpContext context)
    {
        throw new InvalidOperationException("InvalidOperationException");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

另外,module類別除了繼承IHttpModule以外,必須再繼承IRequiresSessionState。

Anyway,這個方法讓我成功取到Session.SessionID,原理之後再慢慢思考。


2012年10月16日 星期二

系統Memo - IIS Server網站掛載HTTP Module

1. 將C#程式編譯成.dll檔,放置在網站目錄下bin資料夾,與web.config層級相同。
2. web.config設定方式如下:
< configuration > 
< system.web >
< customErrors mode="Off" />
< /system.web >
< system.webServer >
< modules >
< add type="NameSpace.ModuleName" name="NameSpace" />
< /modules >
< /system.webServer >
< /configuration >

3. 採用介面為IHttpModules。