2013年7月4日

【Android】AES加密演算法 - 加密與解密

哈囉~各位Android安卓開發者大家好啊 ^^

小黑人今天要與大家分享的是"AES",或許可能會有許多開發者不知道這是什麼東西,哈哈~不要緊張,其實小黑人一開始也不知道AES是什麼,但是經過了解與實作之後總算是知道了,好~小黑人先向大家說明一下什麼是AES,AES簡單來說就是一個"加密演算法",因為在網路傳輸資料時為了保護資訊安全個人資料等,通常都會使用資訊加密讓文字產生另一種文字組合以保障資訊安全,加密後的文字是無法辨認的,最後資訊傳達後再進行解密即可,AES加密演算目前應該算是全球最普及的加密格式,AES加密演算也較不易破解,因為AES可自定義"16位的英數組合位元"、"32位的英數組合Key欄位",有各自的對應欄位也較為謹慎,至於要怎麼使用AES加密演算呢?小黑人接下來向大家實作說明~ ^^

※ AES加密演算直接在程式端(.java)撰寫即可。
1.首先要在程式端先加入下面兩個Function函式,這兩個Function是加密與解密運用的重要演算組成角色,Encrypt為加密,Decrypt為解密
//AES加密,帶入byte[]型態的16位英數組合文字、32位英數組合Key、需加密文字
private static byte[] EncryptAES(byte[] iv, byte[] key,byte[] text)
{
  try
  {
    AlgorithmParameterSpec mAlgorithmParameterSpec = new IvParameterSpec(iv);
    SecretKeySpec mSecretKeySpec = new SecretKeySpec(key, "AES");
    Cipher mCipher = null;
    mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    mCipher.init(Cipher.ENCRYPT_MODE,mSecretKeySpec,mAlgorithmParameterSpec);
       
    return mCipher.doFinal(text);
  }
  catch(Exception ex)
  {
    return null;
  }
}

//AES解密,帶入byte[]型態的16位英數組合文字、32位英數組合Key、需解密文字
private static byte[] DecryptAES(byte[] iv,byte[] key,byte[] text)
{
  try
  {
    AlgorithmParameterSpec mAlgorithmParameterSpec = new IvParameterSpec(iv);
    SecretKeySpec mSecretKeySpec = new SecretKeySpec(key, "AES");
    Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    mCipher.init(Cipher.DECRYPT_MODE,
                 mSecretKeySpec,
                 mAlgorithmParameterSpec);
       
    return mCipher.doFinal(text);
  }
  catch(Exception ex)
  {
    return null;
  }
}
2.實作進行加密與解密 :
※ 首先定義好16位的英數組合位元、32位的英數組合Key欄位、欲加密的文字
//16位的英數組合位元,可自行填寫 (下為小黑人暫訂)
//32位的英數組合Key欄位,可自行填寫 (下為小黑人暫訂)
//欲進行加密的文字字串
private final static String IvAES = "1234567890abcdef" ;
private final static String KeyAES = "12345678901234567890123456789012";
private final static String TextAES = "小黑人的Android教室 !";
A. 加密 :
//將IvAES、KeyAES、TextAES轉成byte[]型態帶入EncryptAES進行加密,再將回傳值轉成字串
byte[] TextByte = EncryptAES(IvAES.getBytes("UTF-8"), KeyAES.getBytes("UTF-8"), TextAES.getBytes("UTF-8"));
String TEXT = Base64.encodeToString(TextByte, Base64.DEFAULT);
//加密字串結果為 : xq/WqrKuXIqLxw1BM4GJoAqPQp6Zh+vqLykVAj2GHFY=
B. 解密 :
//將IvAES、KeyAES、Text(加密文字:xq/WqrKuXIqLxw1BM4GJoAqPQp6Zh+vqLykVAj2GHFY=)轉成byte[]型態帶入DecryptAES進行解密,再將回傳值轉成字串
byte[] TextByte = DecryptAES(IvAES.getBytes("UTF-8"), KeyAES.getBytes("UTF-8"),Base64.decode(TEXT.getBytes("UTF-8"), Base64.DEFAULT));
String TEXT = new String(TextByte,"UTF-8");
//解密字串結果為 : 小黑人的Android教室 !
畫面預覽 :
以上就是AES加密演算的加密與解密運用,大家可以試試看喔 ^^
一些重要的資訊傳輸就要記得將資料加密喔。
如有任何問題都可以和小黑人一起交流討論喔~謝謝大家~! XDD

42 則留言:

  1. 請問要怎麼做才能變成可在APP畫面上用手動輸入文字進EditText裡讓它去做加密呢? 我試了很久都不成功,請版主指點迷津,謝謝。

    回覆刪除
    回覆
    1. 不好意思喔,小黑人最近在研究開發一些新技術元件,
      ...現在才看到留言!!! 抱歉~~~ >"<
      哈~~針對您的問題,EditText進行文字加密,小黑人這邊幫您分析兩種行為,
      第一種是EditText全部輸入完畢後取出文字進行加密,
      第二種是即時加密(就是在輸入的過程中就即時取出EditText文字進行加密)。

      第一種方法作法為:
      將TextAES原本文字變更成EditText的輸入文字,
      TextAES = mEditText.getText().toString();
      以上即可進行EditText文字內容加密。

      第二種方法作法為:
      將TextAES原本文字變更成EditText即時的輸入文字
      首先要加入EditText輸入字元的觸發動作,方法如下,

      mEditText.addTextChangedListener(new TextWatcher()
      {
      @Override
      public void onTextChanged(CharSequence s, int start, int before,int count)
      {
      TextAES = s.toString();
      //這樣TextAES就是EditText輸入任何字元的內容文字。
      }

      @Override
      public void afterTextChanged(Editable s)
      {
      }

      @Override
      public void beforeTextChanged(CharSequence s, int start, int count,int after)
      {
      }
      });
      以上方法即可即時將EditText文字進行加密。

      以上解決方法您可以試試看唷!
      感謝您的提問~! ^^

      刪除
  2. 版主您好,其實我就是用TextAES = mEditText.getText().toString(); 這種方法燒入手機,app一啟動就crash掉了,所以才詢求版主幫忙,我會繼續試試看看第二種再向您請教,謝謝

    回覆刪除
    回覆
    1. 您好,一啟動APP尚未執行加密動作,所以應該不至於程式錯誤,請您先參考下方範例!
      感謝您~ ^^

      刪除
  3. 我單獨這樣子做:
    testEncrypt.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    // TODO Auto-generated method stub
    TextAES = testEncrypt.getText().toString();
    Toast.makeText(MainTwo.this, TextAES , Toast.LENGTH_LONG).show();
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
    int after) {
    // TODO Auto-generated method stub
    }

    @Override
    public void afterTextChanged(Editable s) {
    // TODO Auto-generated method stub

    }
    });
    TextAES 裡是有我輸入的值,也可以toast出來。
    但是如果再把版主的加密解密程式碼套進去一起燒錄的話,app在手機裡就會打不開,crash掉。

    感覺像是這行byte[] TextByte = EncryptAES(IvAES.getBytes(), KeyAES.getBytes(), TextAES.getBytes());
    最後面的TextAES.getbyte這邊無法把上面edittext監聽器裡的
    TextAES裡的值帶進加密那邊做運算。

    請版主幫小弟解惑 謝謝

    回覆刪除
    回覆
    1. 你好,小黑人自己有實作一次,但是小黑人可以正常執行不會出現錯誤!
      小黑人分析您說的一啟動APP就會出現錯誤的原因,
      不知您在layout的xml是否有針對EditText元件進行id命名呢?
      然後在程式裡需要findViewById EditText元件,
      因為在一開啟APP時並尚未做任何加密的動作,所以不至於會出現錯誤的狀況。
      小黑人自己的實作範例給你參考 :
      layout:


      src:
      private EditText mEditText;

      mEditText = (EditText) findViewById(R.id.edit);

      mBtn1.setOnClickListener(new OnClickListener()
      {
      @Override
      public void onClick(View arg0)
      {
      try
      {
      byte[] TextByte = EncryptAES(IvAES.getBytes("UTF-8"), KeyAES.getBytes("UTF-8"), mEditText.getText().toString().getBytes("UTF-8"));
      TEXT = Base64.encodeToString(TextByte, Base64.DEFAULT);
      mText1.setText(TEXT);
      }
      catch(UnsupportedEncodingException e)
      {
      }
      }
      });

      P.S. 原本的TextAES就不使用,直接用mEditText.getText().toString()取代!

      以上的方法小黑人是可以執行,再請您試試看是否成功,如果還是出現錯誤,
      就要查看LogCat裡錯誤的原因,可以再告知小黑人! ^^

      感謝您~

      刪除
    2. layout部分消失了XDD
      再補一次,
      layout:
      < EditText
      android:id="@+id/edit"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      / >

      刪除
    3. 不好意思 這是加密的部分
      那解密的不分該怎麼寫呢?

      刪除
  4. 版主您好:
    關於8/19向您提的問題,我照著您的解決方法,經測試後已可以由Edittext讀取到手動輸入的文字進入加密程序,加解密都可正常運作,實著感激小黑人大大您的協助。
    有點疑惑的是不知為什麼把轉byte那兩行放在button裡執行就沒問題,放在外面運行就會失敗,這點真的滿奇妙的。

    回覆刪除
    回覆
    1. 您好,其實針對您的疑惑小黑人的想法是轉byte這段放在button內時,
      EditText已經有輸入文字了,所以進行轉換是沒有問題的,
      但是如果放在外面,代表程式一執行就會進行轉換的動作,一開始EditText尚未輸入文字,所以取出來的值是空的再進行轉換就會出現錯誤!

      感謝您的提問! ^^

      刪除
    2. 原來是直接抓到空值就進行轉換才出錯呀...這樣我懂了。
      想再請問一下,能否用甲方手機來做文字加密後,再由乙方的手機來解密出來,這樣可行嗎?不知道版主有沒有試過?

      刪除
    3. 您好,在不同的裝置進行加密解密是可行可以的!!!
      只要加密與解密的IvAES與KeyAES帶入一樣的格式內容即可。
      原因是加密是由IvAES與KeyAES進行轉換,相對的解密也用一樣的就可以轉回來囉!!!

      感謝您的提問~ ^^

      刪除
    4. 好的,謝謝,我來試試看。

      刪除
    5. 版主您好,我這樣子寫出錯了,請問能幫我看看嗎?
      DecryptBtn.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
      // TODO Auto-generated method stub
      TEXT = EncryptTxt.getText().toString();
      byte[] TextByte1 = DecryptAES(IvAES.getBytes(), KeyAES.getBytes(),Base64.decode(TEXT.getBytes(), Base64.DEFAULT));
      String TEXT1 = new String(TextByte1);
      Toast.makeText(MainActivity.this, TEXT1 , Toast.LENGTH_LONG).show();
      }
      });

      刪除
    6. 我是把加密的Function拿掉後,換成可以手動輸入另一隻手機加密好的文字,讓解密Function可以直接把我輸入的文字解開,但是打開APP就出錯跳掉了...

      刪除
    7. 哈囉~好久不見...小黑人之前比較忙一點,真的很抱歉!
      首先根據您的問題小黑人向您解釋,
      出錯的部分小黑人覺得是
      byte[] TextByte1 = DecryptAES(IvAES.getBytes(), KeyAES.getBytes(),Base64.decode(TEXT.getBytes(), Base64.DEFAULT));
      String TEXT1 = new String(TextByte1);
      這個部分有問題。
      在轉碼的過程裡還不需要Base64.decode(TEXT.getBytes(), Base64.DEFAULT)編碼,
      因為你在轉碼的過程中型態還是byte[],小黑人沒有試過會不會出錯,但小黑人看到後就覺得轉換過程中順序不一樣,所以您可以試試看文章內所寫的順序試試看喔^^

      感謝您的提問!

      刪除
    8. 哈囉~好久不見...小黑人之前比較忙一點,真的很抱歉!
      首先根據您的問題"打開APP就出錯跳掉"小黑人向您解釋,
      在開啟app時其實並未做任何加密解密的動作,
      所以正常是不會發生錯誤的狀況喔,
      但是,如果出現錯誤的狀況發生,小黑人猜想...
      應該是一開始開啟app時就已經在做加減密的動作了,
      但是輸入框還尚未帶入加減密的文字就進行轉碼,所以會造成錯誤。
      這是小黑人的初步分析,再請您測試看看,如果還是不行的話,
      可能要麻煩您要貼上錯誤訊息讓小黑人與您一起研究。

      感謝您的提問 ! ^^

      刪除
    9. 謝謝大大費心的幫忙

      刪除
  5. 哈囉~好久不見...小黑人之前比較忙一點,真的很抱歉!
    首先根據您的問題小黑人向您解釋,
    出錯的部分小黑人覺得是
    byte[] TextByte1 = DecryptAES(IvAES.getBytes(), KeyAES.getBytes(),Base64.decode(TEXT.getBytes(), Base64.DEFAULT));
    String TEXT1 = new String(TextByte1);
    這個部分有問題。
    在轉碼的過程裡還不需要Base64.decode(TEXT.getBytes(), Base64.DEFAULT)編碼,
    因為你在轉碼的過程中型態還是byte[],小黑人沒有試過會不會出錯,但小黑人看到後就覺得轉換過程中順序不一樣,所以您可以試試看文章內所寫的順序試試看喔^^

    感謝您的提問!

    回覆刪除
  6. 版主好~
    請問"16位的英數組合位元"和"32位的英數組合Key欄位"
    分別指甚麼呢?
    後者是指加密金鑰嗎?
    謝謝你:))

    回覆刪除
    回覆
    1. 您好,很抱歉這麼久才回覆您!
      哈哈,小黑人這段時間忙著處理專題研究,真不好意思...
      根據您的提問小黑人向您解釋,
      16位元英數組合算是"編碼組合",也就是由這16位元進行拆解和組合。
      32位元英數組合算是"金鑰組合",也就是說藉由此組金鑰來進行編碼加減密。

      感謝您的提問喔!

      刪除
  7. 您好請問一下
    我server 是用在點上面的
    然後我import
    android.util.Base64

    但最後在解密的時候卻發現
    TextByte= null
    java.lang.NullPointerException
    請問樓主有遇過這個問題嗎?

    回覆刪除
    回覆
    1. 抱歉更正
      適用在電腦上面

      刪除
    2. 您好,
      正常來說應該是不會有空值的狀況發生(Null),
      可以麻煩您再確認是否有帶入文字呢(Text)?
      如果沒有給系統文字但卻要解析的話就可能會出錯,
      感謝您的留言!

      刪除
  8. 您好請問一下

    這種方法有辦法使用在語音的加密上嗎?

    或者語音又其他的方法可以實作AES加密?

    回覆刪除
    回覆
    1. 您好,很抱歉這麼久才回覆您,
      根據您的提問小黑人與您解釋,
      如果您是用Intent方式藉由內建系統進行語音分析的話應該是沒辦法進行加密,
      因為語音分析的部分皆由系統自行處理,所以我們應該無法控制加密的部分,
      但是如果語音分析這塊是自己作處理的話就可以加入加密的部分,
      因為這樣程式控制權才會都在自己身上.

      感謝您的提問!

      刪除
  9. 請問 加密的部分 如果要改成 NoPadding
    哪邊要修改呢?

    回覆刪除
    回覆
    1. 您好,很抱歉這麼久才回覆您,
      根據您的問題小黑人與您解釋,
      您是說加密這區塊的畫面Layout不要有Padding嗎?
      這只要在Layout裡把Padding設定移除就可以囉,
      小黑人範例裡的Layout原本是有加入Padding的.

      感謝您的提問!

      刪除
  10. 可以給 程式範例的 下載點 嗎?
    很多元件的細節 不是很清楚

    回覆刪除
    回覆
    1. 感謝您的回覆,小黑人整理好範例程式碼後就會放上去囉!

      刪除
  11. 不好意思 請問一下用UTF8的用意是甚麼?
    如果改用其他的 UTF16 Big5 會有甚麼副作用的缺點嗎?

    回覆刪除
    回覆
    1. 您好,根據您的提問小黑人與您解釋,
      UTF-8是編碼的一種,主要是設定字元是要由哪種編碼所組成,
      目前UTF-8的編碼形式較廣泛使用,所以小黑人選擇這個編碼,
      副作用是沒有,UTF-16與Big5皆可使用,
      但要注意的地方是加密的字元編碼與解密的字元編碼要一致.

      感謝您的提問.

      刪除
  12. 想再問個問題
    Iv要如何讓他隨機產生
    因為加密出來的密文都一樣
    還是說本來就是要固定IV值?
    Key也是用隨機產生嗎 還是固定?

    回覆刪除
    回覆
    1. 您好,針對您提出的疑問小黑人與您解答,
      其實這兩個值都是您自己設定的,要用隨機或固定都可以,
      因為加密就是要保護這個內容,所以只有您知道IV值和Key值是什麼,
      唯一要注意的是加密時用的IV值和Key值要知道是什麼,不然會解不出來。
      至於您說要隨機產生值的話,可以用隨機變數取字母或數字組成。

      感謝您的提問!

      刪除
  13. 作者已經移除這則留言。

    回覆刪除
  14. 您好想請問下 AES 有分128 256 ,您的教學是屬哪一種。還是只是金鑰長度差異。初學,請指導感謝您。

    回覆刪除
    回覆
    1. key = 32個char = 32 byte = 32*8 bit = 256 bit

      刪除
  15. 我是初學這,請教如果client是瀏覽器,今天由js端使用aes加密傳送data到server,會遇到問題。

    js的code無法如同server端受保護,這樣是不是做aes加密也是枉然,因為key,iv會被竊取緣故?

    回覆刪除
  16. 請問黑人先生您有幫人做AES 解密嗎?
    或著請您介紹高手 我願意付費
    謝謝
    莊 0932135947

    回覆刪除
  17. 請問黑人先生,有幫人做AES 128的加解密嗎?比方說:我在Android手機做了一個app,藉由WiFi控制家中的電器產,但是受控端只能燒寫純HTML的程式碼,網上爬文一下,是有PHP範例的,但沒找到HTML的.如此視頻網址 https://www.youtube.com/watch?v=ns_wJ8Z1kw8&t=23s
    還請不吝賜教.感謝觀看

    回覆刪除
  18. 可以用, 謝謝大大的分享教學

    回覆刪除

謝謝大家支持,有任何問題都可以和小黑人一起討論!