大家好,最近的一個項目中為了防止有人直接提交報文,,所以團隊打算在報文的傳輸過程中引入RSA加密的方式,,用以防止這種直接通過報文的提交來進行功能的操作。
下面說下項目的背景,。項目是一款采用H5架構的APP,,我們主要來說明Android端的情況,主要分為三個部分,,首先是一個Android的殼,,是通過原生Android用來承載H5界面以及樣式表等文件,其中涉及到版本控制,,加密解密等功能,,其次是第二部分,第二部分是前臺部分,,涉及到頁面,,Action層,第三部分是后臺部分,,涉及到Service,,其中主要操作包括數(shù)據(jù)的操作,接口的調(diào)用等,。我們想在原生Android層進行報文的加密,,在后臺進行解密校驗等。下面是在原生Android部分做的一個RSA加密解密的Demo,,其中還涉及到關于RSA中需要的公鑰私鑰的生成,。
注意:通過RSA加密之后的密文是一串亂碼,所以在這個Demo項目中我們給了個Base64的編碼方式,,以便有個更好的閱讀和編碼方式不至于出問題,。同樣的,,在解密的時候,我們需要把這個Base64的先解碼,,在使用私鑰進行解密,。
首先我們來看下在原生Android下關于RSA加密解密的Demo的演示圖片
整個布局文件包含4個EditText,2個Button,,在最上面的EditText上輸入需要加密的明文,,點擊加密,把加密的密文放到第二個EditText上,,之后點擊解密,,把加密的密文在解密,整個過程所耗費的時間戳,,打印在最下面的EditText上,。下面是布局代碼:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas./apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <EditText
- android:id="@+id/et1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10" >
- <requestFocus />
- </EditText>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="84dp"
- android:orientation="horizontal" >
- <Button
- android:id="@+id/btn1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="加密" />
- <Button
- android:id="@+id/btn2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="解密" />
- </LinearLayout>
- <EditText
- android:id="@+id/et2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10" />
- <EditText
- android:id="@+id/et3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10" />
- <TextView
- android:id="@+id/textView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="時間:" />
- <EditText
- android:id="@+id/et4"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10" />
- </LinearLayout>
關于布局,很簡單,,我們就不多說了,。我們來看看MainActivity,在MainActivity中,,我們加載布局組件,。然后定義2個按鈕的點擊事件
- <span style="font-size:14px;">private void initView()
- {
- btn1 = (Button) findViewById(R.id.btn1);
- btn2 = (Button) findViewById(R.id.btn2);
- btn1.setOnClickListener(this);
- btn2.setOnClickListener(this);
-
- et1 = (EditText) findViewById(R.id.et1);
- et2 = (EditText) findViewById(R.id.et2);
- et3 = (EditText) findViewById(R.id.et3);
- et4 = (EditText) findViewById(R.id.et4);
- }
-
- @Override
- public void onClick(View v)
- {
- switch (v.getId())
- {
- // 加密
- case R.id.btn1:
- String source = et1.getText().toString().trim();
- try
- {
- //根據(jù)字符串String得到PublicKey
- PublicKey publicKey = RSAUtils.loadPublicKey(PUCLIC_KEY);
- // 加密
- byte[] encryptByte = RSAUtils.encryptData(source.getBytes(), publicKey);
- // 因為前面公鑰是使用的Base64加密的,這里需要使用Base64解密
- String afterencrypt = Base64Utils.encode(encryptByte);
- et2.setText(afterencrypt);
- } catch (Exception e)
- {
- e.printStackTrace();
- }
- break;
- // 解密
- case R.id.btn2:
- long now_before = new Date().getTime();
- String encryptContent = et2.getText().toString().trim();
- try
- {
-
- // </span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:10px;">根據(jù)字符串String得到PublicKey</span></span><span style="font-size:14px;">
- PrivateKey privateKey = RSAUtils.loadPrivateKey(PRIVATE_KEY);
- // 因為RSA加密后的內(nèi)容經(jīng)Base64再加密轉換了一下,,所以先Base64解密回來再給RSA解密,,加密解密的過程中數(shù)據(jù)類型是Byte數(shù)組
- byte[] decryptByte = RSAUtils.decryptData(Base64Utils.decode(encryptContent), privateKey);
- String decryptStr = new String(decryptByte);
- et3.setText(decryptStr);
- } catch (Exception e)
- {
- e.printStackTrace();
- }</span>
- <span style="font-size:14px;"><span style="white-space:pre"> </span>//時間戳
- long now_after = new Date().getTime();
- long date = now_after - now_before;
- Toast.makeText(getApplicationContext(), date+"", Toast.LENGTH_LONG).show();
- et4.setText(""+date);
- break;
- default:
- break;
- }
- }</span>
在initView()方法中,我們通過findViewById找到按鈕和EditText組件,。在onClick(View v)中我們來通過Activity接口的OnClickListener來定義加密和解密的按鈕觸發(fā)事件,。在加密的過程中,我們根據(jù)前面定義的兩個String常量publicKey和私鑰的primaryKey調(diào)用RSAUtil的loadPublicKey方法,,來生成的PublicKey,,解密的過程中,也是如此,,下面是loadPublicKey方法,。
- <span style="white-space:pre"> </span>/**
- * 從字符串中加載公鑰
- *
- * @param publicKeyStr
- * 公鑰數(shù)據(jù)字符串
- * @throws Exception
- * 加載公鑰時產(chǎn)生的異常
- */
- public static PublicKey loadPublicKey(String publicKeyStr) throws Exception
- {
- try
- {
- byte[] buffer = Base64Utils.decode(publicKeyStr);
- KeyFactory keyFactory = KeyFactory.getInstance(RSA);
- X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
- return (RSAPublicKey) keyFactory.generatePublic(keySpec);
- } catch (NoSuchAlgorithmException e)
- {
- throw new Exception("無此算法");
- } catch (InvalidKeySpecException e)
- {
- throw new Exception("公鑰非法");
- } catch (NullPointerException e)
- {
- throw new Exception("公鑰數(shù)據(jù)為空");
- }
- }
根據(jù)傳入的字符串公鑰,拿到PublicKey,。首先給字符串公鑰的進行Base64的解碼,,拿到Byte[],根據(jù)X509EncodedKeySpec給定的編碼密鑰創(chuàng)建一個新的 X509EncodedKeySpec。使用keyFactory的genratePublic方法返回PublicKey,。這樣就拿到了publickey,。
拿到了publicKey,我們調(diào)用RSAUtil中的encryptData(),,來進行加密的操作,。把加密的結果方式EditText上用以顯示,。下面我們看下解密的方法
- /**
- * 用公鑰加密 <br>
- * 每次加密的字節(jié)數(shù),不能超過密鑰的長度值減去11
- *
- * @param data
- * 需加密數(shù)據(jù)的byte數(shù)據(jù)
- * @param pubKey
- * 公鑰
- * @return 加密后的byte型數(shù)據(jù)
- */
- public static byte[] encryptData(byte[] data, PublicKey publicKey)
- {
- try
- {
- Cipher cipher = Cipher.getInstance(RSA);
- // 編碼前設定編碼方式及密鑰
- cipher.init(Cipher.ENCRYPT_MODE, publicKey);
- // 傳入編碼數(shù)據(jù)并返回編碼結果
- return cipher.doFinal(data);
- } catch (Exception e)
- {
- e.printStackTrace();
- return null;
- }
- }
根據(jù)Cipher設置密文的類型為RSA,,調(diào)用init方法設置編碼前編碼方式和密鑰,,傳入編碼數(shù)據(jù)并且放回編碼結果。在把編碼結果放到EditText上用以顯示,。
解密的方法大同小異,。
- /**
- * 從字符串中加載私鑰<br>
- * 加載時使用的是PKCS8EncodedKeySpec(PKCS#8編碼的Key指令)。
- *
- * @param privateKeyStr
- * @return
- * @throws Exception
- */
- public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception
- {
- try
- {
- byte[] buffer = Base64Utils.decode(privateKeyStr);
- // X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
- KeyFactory keyFactory = KeyFactory.getInstance(RSA);
- return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
- } catch (NoSuchAlgorithmException e)
- {
- throw new Exception("無此算法");
- } catch (InvalidKeySpecException e)
- {
- throw new Exception("私鑰非法");
- } catch (NullPointerException e)
- {
- throw new Exception("私鑰數(shù)據(jù)為空");
- }
- }
- /**
- * 用私鑰解密
- *
- * @param encryptedData
- * 經(jīng)過encryptedData()加密返回的byte數(shù)據(jù)
- * @param privateKey
- * 私鑰
- * @return
- */
- public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey)
- {
- try
- {
- Cipher cipher = Cipher.getInstance(RSA);
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- return cipher.doFinal(encryptedData);
- } catch (Exception e)
- {
- return null;
- }
- }
最后給大家一串字符串的公鑰和私鑰用以測試使用
- private static String PUCLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHpicKssPjh3p1aHEtLQrGjvFqYQe9Qwj+P56dj8fnYa3xamxzwZrHzhZCgjjxKBOgTyhwUcCAnjMxp9laIf1KkIvE2RN5Nkaq6NvW5BZEvqUMW7BEh4yiZdAXK+MjLWm2Qhf8j0YEI5R4DEYCjshMJ0wYVpM05K8kNNFP7B+F9QIDAQAB";
- //私鑰
- private static String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIemJwqyw+OHenVocS0tCsaO8WphB71DCP4/np2Px+dhrfFqbHPBmsfOFkKCOPEoE6BPKHBRwICeMzGn2Voh/UqQi8TZE3k2Rqro29bkFkS+pQxbsESHjKJl0Bcr4yMtabZCF/yPRgQjlHgMRgKOyEwnTBhWkzTkryQ00U/sH4X1AgMBAAECgYBj2pB01KFUdV9U3Bwr6DM9dO4Lo/+hd55AIq7tR3EdR49W3kOVdpgsqu1B6kBmbVz9LigTfmqZg1smG2vpaInd7OLLjgZzumOrArvLQdwScNM5Dn+kZIBJ7N5iVag5aP2KCX9AM/CIqyW6J0nfB9KUffU2YkmE+ZdZurVWm3Y0JQJBAM8pRUVUIu4jfKrntIb3X7Ffo4OoP/ODVAeDmQkJaaNqmDpycm+SqyKZDqZBC8PaFBRwW9UDVcuZvL7lNcmoS+cCQQCnoO6S0ZkQhgWpS0AmmwcCK/nsmc2XhOLf1rQ1dm4EKDSEvAkG67cOsR/2Usl6IYlGlQKJyZwabKbC3Z/WZgPDAkA+5n4U9d4BRp8k2WO0E0pn9e0VHbIFQ1vxSCDgYI5Fwyjjnjpm7DawM58CFf/3gLDWH+OSQwf64PwxTjFNwJ8DAkAw8gy3UfwflwKQLCjPHPUu7ShMrZwaYfLc6RQ1iB8Xl6W+HCmGm80XvSBYDFRIFQLAWUIkeXnbPV50B8JkF+WBAkA7oVRBgJcDcx7KBuRCwrd+goCslV6hz36Uc4CdJsZfyVDcLFthFgFGoePWSPh08POGJIHrwt+SrWlKLsoXWNbu"
下面把項目源碼(RSADemo)上傳到CSDN供大家下載,,在提供一個項目(Keys)可以用來生成公鑰和私鑰,。
RSADemo:http://download.csdn.net/detail/a514224717/9216251
Keys:http://download.csdn.net/detail/a514224717/9216337
|