閱讀34 返回首頁    go 京東網上商城


android VPN編程

VPN的英文全稱是“Virtual Private Network”,翻譯過來就是“虛擬專用網絡”。顧名思義,虛擬專用網絡我們可以把它理解成是虛擬出來的企業內部專線。它可以通過特殊的加密的通訊協議在連接在Internet上的位於不同地方的兩個或多個企業內部網之間建立一條專有的通訊線路,就好比是架設了一條專線一樣,但是它並不需要真正的去鋪設光纜之類的物理線路。這就好比去電信局申請專線,但是不用給鋪設線路的費用,也不用購買路由器等硬件設備。VPN技術原是路由器具有的重要技術之一,目前在交換機,防火牆設備或WINDOWS2000等軟件裏也都支持VPN功能,一句話,VPN的核心就是在利用公共網絡建立虛擬私有網。虛擬專用網(VPN)被定義為通過一個公用網絡(通常是因特網)建立一個臨時的、安全的連接,是一條穿過混亂的公用網絡的安全、穩定的隧道。虛擬專用網是對企業公司分支機構、商業夥伴及供應商同公司的內部網建立可信的安全連接,並保證數據的安全傳輸。

 

關於Android VPN編程,網上的資料比較少,本人最近有一個關於在Android平台下開發VPN 客戶端的項目,在項目剛開始的時候,進行比較艱難,主要是android(4.0之前)的VPN領域鮮有API可供直接調用,也沒有官方的說明文檔。經過將近一個星期的研究,終於有些頭緒,在此本人願將一些經驗與大家共享。


為了研究android vpn,本人下載一些apk,通過反編譯工具apktool,查看了一些代碼片段,雖然反編譯之後的代碼不易於閱讀,但是還是提供了一些有用的信息。通過對apk的

debug,本人發現很多apk都引入了xinkvpn開發包。非常感謝xinkvpn的開發者,抽取並封裝了Android係統不對外訪問的API,並提供了完整的代碼,到目前為止可能對於SDK10之後的訪問還存在問題,對於SDK14之前可以在此基礎上進行一些修改,對於SDK14(Android4.0)後,則無法調用到公開的Android API接口,因此需要另外的研究。


需要注意的是,對於android 4.0之前的版本,如果采用l2tp secret,ipsec 方式,則需要獲得root權限,並寫入Keystore;對於PPTP和L2tp則不需要,但是因為其采用的是非加密方式,因此安全性要差一些。


關於xinkvpn,有興趣的讀者可以到https://github.com/xinthink/xinkvpn進行下載並運行,在此不再重複。


本人的項目要求是用戶不需要輸入用戶名/密碼/服務器IP等和VPN設置有關的參數,就可以直接連接到VPN。因此鏈接VPN的操作要在後台進行,這樣就要對賬戶進行封裝。當然本文的代碼也可以供非此類情況的使用。


首先建立一個保存VPN Profile的抽象類,供PPTP,L2tp,L2tpIpsecPsk等擴展,此抽象類如下

  1. package vink.vpn.whyonly.editor;  
  2.   
  3. import xink.vpn.VpnProfileRepository;  
  4. import xink.vpn.wrapper.InvalidProfileException;  
  5. import xink.vpn.wrapper.KeyStore;  
  6. import xink.vpn.wrapper.VpnProfile;  
  7. import xink.vpn.wrapper.VpnState;  
  8. import android.content.Context;  
  9.   
  10. /** 
  11.  * @author Whyonly 
  12.  *  
  13.  */  
  14. public abstract class OWLVpnProfileEditor {  
  15.   
  16.     private VpnProfile profile;  
  17.     private VpnProfileRepository repository;  
  18.     private KeyStore keyStore;  
  19.     private Runnable resumeAction;  
  20.   
  21.     protected Context mContext;  
  22.   
  23.     public OWLVpnProfileEditor(final Context context) {  
  24.         mContext = context;  
  25.         repository = VpnProfileRepository.getInstance(context);  
  26.         keyStore = new KeyStore(context);  
  27.     }  
  28.   
  29.     public void onSave() {  
  30.         try {  
  31.             profile = createProfile();  
  32.             populateProfile();  
  33.             saveProfile();  
  34.         } catch (InvalidProfileException e) {  
  35.             throw e;  
  36.         }  
  37.     }  
  38.   
  39.     private void populateProfile() {  
  40.         profile.setState(VpnState.IDLE);  
  41.         doPopulateProfile();  
  42.         repository.checkProfile(profile);  
  43.     }  
  44.   
  45.     private void saveProfile() {  
  46.         repository.addVpnProfile(profile);  
  47.     }  
  48.   
  49.     @SuppressWarnings("unchecked")  
  50.     protected <T extends VpnProfile> T getProfile() {  
  51.         return (T) profile;  
  52.     }  
  53.       
  54.     protected abstract VpnProfile createProfile();  
  55.   
  56.     protected abstract void doPopulateProfile();  
  57. }  

PPTP的擴展如下


  1. package vink.vpn.whyonly.editor;  
  2.   
  3. import xink.vpn.wrapper.PptpProfile;  
  4. import xink.vpn.wrapper.VpnProfile;  
  5. import android.content.Context;  
  6.   
  7. /** 
  8.  * @author Whyonly 
  9.  *  
  10.  */  
  11. public class OWLPptpProfileEditor extends OWLVpnProfileEditor {  
  12.   
  13.     public OWLPptpProfileEditor(final Context context) {  
  14.         super(context);  
  15.     }  
  16.   
  17.     @Override  
  18.     protected VpnProfile createProfile() {  
  19.         return new PptpProfile(mContext);  
  20.     }  
  21.   
  22.   
  23.     @Override  
  24.     protected void doPopulateProfile() {  
  25.         PptpProfile profile = getProfile();  
  26.         profile.setName("OWLPPTP");  
  27.         profile.setServerName("0.0.0.0");  
  28.         profile.setDomainSuffices("8.8.8.8");  
  29.         profile.setUsername("whyonly");  
  30.         profile.setPassword(".....");  
  31.         profile.setEncryptionEnabled(true);  
  32.     }  
  33.   
  34. }  

請自行填入關於PPTP的賬戶信息,如果沒有的話,可以到網上找一個VPN的免費代理,申請帳號。


L2TP IP Sec的代碼如下,

  1. package vink.vpn.whyonly.editor;  
  2.   
  3. import xink.vpn.wrapper.L2tpIpsecPskProfile;  
  4. import xink.vpn.wrapper.VpnProfile;  
  5. import android.content.Context;  
  6.   
  7. /** 
  8.  * @author Whyonly 
  9.  *  
  10.  */  
  11. public class OWLL2tpIpsecPskProfileEditor extends OWLVpnProfileEditor {  
  12.   
  13.     public OWLL2tpIpsecPskProfileEditor(final Context context) {  
  14.         super(context);  
  15.     }  
  16.   
  17.     @Override  
  18.     protected VpnProfile createProfile() {  
  19.         return new L2tpIpsecPskProfile(mContext);  
  20.     }  
  21.   
  22.   
  23.     @Override  
  24.     protected void doPopulateProfile() {  
  25.         L2tpIpsecPskProfile p = getProfile();  
  26.   
  27.         p.setName("OWLL2tpIpsecPsk");  
  28.         p.setServerName("0.0.0.0");  
  29.         p.setDomainSuffices("8.8.8.8");  
  30.         p.setUsername("xxxxxxx");  
  31.         p.setPassword("xxxx");  
  32.   
  33.         p.setPresharedKey("xxxxx");  
  34.         boolean secretEnabled = false;  
  35.         p.setSecretEnabled(secretEnabled);  
  36.         p.setSecretString(secretEnabled ? "xxxx" : "");  
  37.     }  
  38.   
  39. }  


請自行填入關於賬戶的相關信息。


建立好賬戶的類之後,就是把賬戶存入Profile,


  1. OWLVpnProfileEditor pptp = new OWLPptpProfileEditor(this);  
  2.             pptp.onSave();  
  3.             OWLVpnProfileEditor lwtpIpsec = new OWLL2tpIpsecPskProfileEditor(this);  
  4.             lwtpIpsec.onSave();  

接著是連接到賬戶
  1. VpnActor actor = new VpnActor(getApplicationContext());  
  2. actor.connect(profile);  

最後需要對連接的狀態進行監聽,


  1. private void registerReceivers() {  
  2.     IntentFilter filter = new IntentFilter();  
  3.     filter.addAction(ACTION_VPN_CONNECTIVITY);  
  4.     stateBroadcastReceiver = new BroadcastReceiver() {  
  5.         @Override  
  6.         public void onReceive(final Context context, final Intent intent) {  
  7.             String action = intent.getAction();  
  8.   
  9.             if (ACTION_VPN_CONNECTIVITY.equals(action)) {  
  10.                 onStateChanged(intent);  
  11.             } else {  
  12.                 Log.d(TAG, "VPNSettings receiver ignores intent:" + intent); //$NON-NLS-1$  
  13.             }  
  14.         }  
  15.     };  
  16.     registerReceiver(stateBroadcastReceiver, filter);  
  17. }  
  18.   
  19. private void onStateChanged(final Intent intent) {  
  20.     //Log.d(TAG, "onStateChanged: " + intent); //$NON-NLS-1$  
  21.   
  22.     final String profileName = intent.getStringExtra(BROADCAST_PROFILE_NAME);  
  23.     final VpnState state = Utils.extractVpnState(intent);  
  24.     final int err = intent.getIntExtra(BROADCAST_ERROR_CODE, VPN_ERROR_NO_ERROR);  
  25.   
  26.     runOnUiThread(new Runnable() {  
  27.         @Override  
  28.         public void run() {  
  29.             stateChanged(profileName, state, err);  
  30.         }  
  31.     });  
  32. }  
  33.   
  34. private void stateChanged(final String profileName, final VpnState state, final int errCode) {  
  35. processing change  

最後更新:2017-04-03 16:49:10

  上一篇:go ViewFlipper實現多頁麵切換
  下一篇:go 配置Mysql用戶權限命令