gse源碼
一 為什麼要加密?
對於傳統的C或C++之類的語言來說 要在Web上保護源代碼是很容易的 只要不發布它就可以 遺憾的是 Java程序的源代碼很容易被別人偷看 只要有一個反編譯器 任何人都可以分析別人的代碼 Java的靈活性使得源代碼很容易被竊取 但與此同時 它也使通過加密保護代碼變得相對容易 我們唯一需要了解的就是Java的ClassLoader對象 當然 在加密過程中 有關Java Cryptography Extension(JCE)的知識也是必不可少的
有幾種技術可以 模糊 Java類文件 使得反編譯器處理類文件的效果大打折扣 然而 修改反編譯器使之能夠處理這些經過模糊處理的類文件並不是什麼難事 所以不能簡單地依賴模糊技術來保證源代碼的安全
我們可以用流行的加密工具加密應用 比如PGP(Pretty Good Privacy)或GPG(GNU Privacy Guard) 這時 最終用戶在運行應用之前必須先進行解密 但解密之後 最終用戶就有了一份不加密的類文件 這和事先不進行加密沒有什麼差別
Java運行時裝入位元組碼的機制隱含地意味著可以對位元組碼進行修改 JVM每次裝入類文件時都需要一個稱為ClassLoader的對象 這個對象負責把新的類裝入正在運行的JVM JVM給ClassLoader一個包含了待裝入類(比如java lang Object)名字的字元串 然後由ClassLoader負責找到類文件 裝入原始數據 並把它轉換成一個Class對象
我們可以通過定製ClassLoader 在類文件執行之前修改它 這種技術的應用非常廣泛??在這里 它的用途是在類文件裝入之時進行解密 因此可以看成是一種即時解密器 由於解密後的位元組碼文件永遠不會保存到文件系統 所以竊密者很難得到解密後的代碼
由於把原始位元組碼轉換成Class對象的過程完全由系統負責 所以創建定製ClassLoader對象其實並不困難 只需先獲得原始數據 接著就可以進行包含解密在內的任何轉換
Java 在一定程度上簡化了定製ClassLoader的構建 在Java 中 loadClass的預設實現仍舊負責處理所有必需的步驟 但為了顧及各種定製的類裝入過程 它還調用一個新的findClass方法
這為我們編寫定製的ClassLoader提供了一條捷徑 減少了麻煩 只需覆蓋findClass 而不是覆蓋loadClass 這種方法避免了重復所有裝入器必需執行的公共步驟 因為這一切由loadClass負責
不過 本文的定製ClassLoader並不使用這種方法 原因很簡單 如果由默認的ClassLoader先尋找經過加密的類文件 它可以找到;但由於類文件已經加密 所以它不會認可這個類文件 裝入過程將失敗 因此 我們必須自己實現loadClass 稍微增加了一些工作量
二 定製類裝入器
每一個運行著的JVM已經擁有一個ClassLoader 這個默認的ClassLoader根據CLASSPATH環境變數的值 在本地文件系統中尋找合適的位元組碼文件
應用定製ClassLoader要求對這個過程有較為深入的認識 我們首先必須創建一個定製ClassLoader類的實例 然後顯式地要求它裝入另外一個類 這就強制JVM把該類以及所有它所需要的類關聯到定製的ClassLoader Listing 顯示了如何用定製ClassLoader裝入類文件
【Listing 利用定製的ClassLoader裝入類文件】
以下是引用片段
// 首先創建一個ClassLoader對象 ClassLoader myClassLoader = new myClassLoader(); // 利用定製ClassLoader對象裝入類文件 // 並把它轉換成Class對象 Class myClass = myClassLoader loadClass( mypackage MyClass ); // 最後 創建該類的一個實例 Object newInstance = myClass newInstance(); // 注意 MyClass所需要的所有其他類 都將通過 // 定製的ClassLoader自動裝入
如前所述 定製ClassLoader只需先獲取類文件的數據 然後把位元組碼傳遞給運行時系統 由後者完成餘下的任務
ClassLoader有幾個重要的方法 創建定製的ClassLoader時 我們只需覆蓋其中的一個 即loadClass 提供獲取原始類文件數據的代碼 這個方法有兩個參數 類的名字 以及一個表示JVM是否要求解析類名字的標記(即是否同時裝入有依賴關系的類) 如果這個標記是true 我們只需在返回JVM之前調用resolveClass
【Listing ClassLoader loadClass()的一個簡單實現】
以下是引用片段
public Class loadClass( String name boolean resolve ) throws ClassNotFoundException { try { // 我們要創建的Class對象 Class clasz = null; // 必需的步驟 如果類已經在系統緩沖之中 // 我們不必再次裝入它 clasz = findLoadedClass( name ); if (clasz != null) return clasz; // 下面是定製部分 byte classData[] = /* 通過某種方法獲取位元組碼數據 */; if (classData != null) { // 成功讀取位元組碼數據 現在把它轉換成一個Class對象 clasz = defineClass( name classData classData length ); } // 必需的步驟 如果上面沒有成功 // 我們嘗試用默認的ClassLoader裝入它 if (clasz == null) clasz = findSystemClass( name ); // 必需的步驟 如有必要 則裝入相關的類 if (resolve && clasz != null) resolveClass( clasz ); // 把類返回給調用者 return clasz; } catch( IOException ie ) { throw new ClassNotFoundException( ie toString() ); } catch( GeneralSecurityException gse ) { throw new ClassNotFoundException( gse toString() ); } }
Listing 顯示了一個簡單的loadClass實現 代碼中的大部分對所有ClassLoader對象來說都一樣 但有一小部分(已通過注釋標記)是特有的 在處理過程中 ClassLoader對象要用到其他幾個輔助方法
findLoadedClass 用來進行檢查 以便確認被請求的類當前還不存在 loadClass方法應該首先調用它
defineClass 獲得原始類文件位元組碼數據之後 調用defineClass把它轉換成一個Class對象 任何loadClass實現都必須調用這個方法
findSystemClass 提供默認ClassLoader的支持 如果用來尋找類的定製方法不能找到指定的類(或者有意地不用定製方法) 則可以調用該方法嘗試默認的裝入方式 這是很有用的 特別是從普通的JAR文件裝入標准Java類時
resolveClass 當JVM想要裝入的不僅包括指定的類 而且還包括該類引用的所有其他類時 它會把loadClass的resolve參數設置成true 這時 我們必須在返回剛剛裝入的Class對象給調用者之前調用resolveClass
三 加密 解密
Java加密擴展即Java Cryptography Extension 簡稱JCE 它是Sun的加密服務軟體 包含了加密和密匙生成功能 JCE是JCA(Java Cryptography Architecture)的一種擴展
JCE沒有規定具體的加密演算法 但提供了一個框架 加密演算法的具體實現可以作為服務提供者加入 除了JCE框架之外 JCE軟體包還包含了SunJCE服務提供者 其中包括許多有用的加密演算法 比如DES(Data Encryption Standard)和Blowfish
為簡單計 在本文中我們將用DES演算法加密和解密位元組碼 下面是用JCE加密和解密數據必須遵循的基本步驟
步驟 生成一個安全密匙 在加密或解密任何數據之前需要有一個密匙 密匙是隨同被加密的應用一起發布的一小段數據 Listing 顯示了如何生成一個密匙 【Listing 生成一個密匙】
以下是引用片段
// DES演算法要求有一個可信任的隨機數源 SecureRandom sr = new SecureRandom(); // 為我們選擇的DES演算法生成一個KeyGenerator對象 KeyGenerator kg = KeyGenerator getInstance( DES ); kg init( sr ); // 生成密匙 SecretKey key = kg generateKey(); // 獲取密匙數據 byte rawKeyData[] = key getEncoded(); /* 接下來就可以用密匙進行加密或解密 或者把它保存 為文件供以後使用 */ doSomething( rawKeyData );步驟 加密數據 得到密匙之後 接下來就可以用它加密數據 除了解密的ClassLoader之外 一般還要有一個加密待發布應用的獨立程序(見Listing ) 【Listing 用密匙加密原始數據】
以下是引用片段
// DES演算法要求有一個可信任的隨機數源 SecureRandom sr = new SecureRandom(); byte rawKeyData[] = /* 用某種方法獲得密匙數據 */; // 從原始密匙數據創建DESKeySpec對象 DESKeySpec dks = new DESKeySpec( rawKeyData ); // 創建一個密匙工廠 然後用它把DESKeySpec轉換成 // 一個SecretKey對象 SecretKeyFactory keyFactory = SecretKeyFactory getInstance( DES ); SecretKey key = keyFactory generateSecret( dks ); // Cipher對象實際完成加密操作 Cipher cipher = Cipher getInstance( DES ); // 用密匙初始化Cipher對象 cipher init( Cipher ENCRYPT_MODE key sr ); // 現在 獲取數據並加密 byte data[] = /* 用某種方法獲取數據 */ // 正式執行加密操作 byte encryptedData[] = cipher doFinal( data ); // 進一步處理加密後的數據 doSomething( encryptedData );步驟 解密數據 運行經過加密的應用時 ClassLoader分析並解密類文件 操作步驟如Listing 所示 【Listing 用密匙解密數據】
// DES演算法要求有一個可信任的隨機數源 SecureRandom sr = new SecureRandom(); byte rawKeyData[] = /* 用某種方法獲取原始密匙數據 */; // 從原始密匙數據創建一個DESKeySpec對象 DESKeySpec dks = new DESKeySpec( rawKeyData ); // 創建一個密匙工廠 然後用它把DESKeySpec對象轉換成 // 一個SecretKey對象 SecretKeyFactory keyFactory = SecretKeyFactory getInstance( DES ); SecretKey key = keyFactory generateSecret( dks ); // Cipher對象實際完成解密操作 Cipher cipher = Cipher getInstance( DES ); // 用密匙初始化Cipher對象 cipher init( Cipher DECRYPT_MODE key sr ); // 現在 獲取數據並解密 byte encryptedData[] = /* 獲得經過加密的數據 */ // 正式執行解密操作 byte decryptedData[] = cipher doFinal( encryptedData ); // 進一步處理解密後的數據 doSomething( decryptedData );
四 應用實例
前面介紹了如何加密和解密數據 要部署一個經過加密的應用 步驟如下
步驟 創建應用 我們的例子包含一個App主類 兩個輔助類(分別稱為Foo和Bar) 這個應用沒有什麼實際功用 但只要我們能夠加密這個應用 加密其他應用也就不在話下
步驟 生成一個安全密匙 在命令行 利用GenerateKey工具(參見GenerateKey java)把密匙寫入一個文件 % java GenerateKey key data
步驟 加密應用 在命令行 利用EncryptClasses工具(參見EncryptClasses java)加密應用的類 % java EncryptClasses key data App class Foo class Bar class
該命令把每一個 class文件替換成它們各自的加密版本
步驟 運行經過加密的應用 用戶通過一個DecryptStart程序運行經過加密的應用 DecryptStart程序如Listing 所示 【Listing DecryptStart java 啟動被加密應用的程序】
以下是引用片段
import java io *; import java security *; import java lang reflect *; import javax crypto *; import javax crypto spec *; public class DecryptStart extends ClassLoader { // 這些對象在構造函數中設置 // 以後loadClass()方法將利用它們解密類 private SecretKey key; private Cipher cipher; // 構造函數 設置解密所需要的對象 public DecryptStart( SecretKey key ) throws GeneralSecurityException IOException { this key = key; String algorithm = DES ; SecureRandom sr = new SecureRandom(); System err println( [DecryptStart: creating cipher] ); cipher = Cipher getInstance( algorithm ); cipher init( Cipher DECRYPT_MODE key sr ); } // main過程 我們要在這里讀入密匙 創建DecryptStart的 // 實例 它就是我們的定製ClassLoader // 設置好ClassLoader以後 我們用它裝入應用實例 // 最後 我們通過Java Reflection API調用應用實例的main方法 static public void main( String args[] ) throws Exception { String keyFilename = args[ ]; String appName = args[ ]; // 這些是傳遞給應用本身的參數 String realArgs[] = new String[args length ]; System array( args realArgs args length ); // 讀取密匙 System err println( [DecryptStart: reading key] ); byte rawKey[] = Util readFile( keyFilename ); DESKeySpec dks = new DESKeySpec( rawKey ); SecretKeyFactory keyFactory = SecretKeyFactory getInstance( DES ); SecretKey key = keyFactory generateSecret( dks ); // 創建解密的ClassLoader DecryptStart dr = new DecryptStart( key ); // 創建應用主類的一個實例 // 通過ClassLoader裝入它 System err println( [DecryptStart: loading +appName+ ] ); Class clasz = dr loadClass( appName ); // 最後 通過Reflection API調用應用實例 // 的main()方法 // 獲取一個對main()的引用 String proto[] = new String[ ]; Class mainArgs[] = { (new String[ ]) getClass() }; Method main = clasz getMethod( main mainArgs ); // 創建一個包含main()方法參數的數組 Object argsArray[] = { realArgs }; System err println( [DecryptStart: running +appName+ main()] ); // 調用main() main invoke( null argsArray ); } public Class loadClass( String name boolean resolve ) throws ClassNotFoundException { try { // 我們要創建的Class對象 Class clasz = null; // 必需的步驟 如果類已經在系統緩沖之中 // 我們不必再次裝入它 clasz = findLoadedClass( name ); if (clasz != null) return clasz; // 下面是定製部分 try { // 讀取經過加密的類文件 byte classData[] = Util readFile( name+ class ); if (classData != null) { // 解密 byte decryptedClassData[] = cipher doFinal( classData ); // 再把它轉換成一個類 clasz = defineClass( name decryptedClassData decryptedClassData length ); System err println( [DecryptStart: decrypting class +name+ ] ); } } catch( FileNotFoundException fnfe ) // 必需的步驟 如果上面沒有成功 // 我們嘗試用默認的ClassLoader裝入它 if (clasz == null) clasz = findSystemClass( name ); // 必需的步驟 如有必要 則裝入相關的類 if (resolve && clasz != null) resolveClass( clasz ); // 把類返回給調用者 return clasz; } catch( IOException ie ) { throw new ClassNotFoundException( ie toString() ); } catch( GeneralSecurityException gse ) { throw new ClassNotFoundException( gse toString() ); } } }對於未經加密的應用 正常執行方式如下 % java App arg arg arg
對於經過加密的應用 則相應的運行方式為 % java DecryptStart key data App arg arg arg
DecryptStart有兩個目的 一個DecryptStart的實例就是一個實施即時解密操作的定製ClassLoader;同時 DecryptStart還包含一個main過程 它創建解密器實例並用它裝入和運行應用 示例應用App的代碼包含在App java Foo java和Bar java內 Util java是一個文件I/O工具 本文示例多處用到了它 完整的代碼請從本文最後下載
五 注意事項
我們看到 要在不修改源代碼的情況下加密一個Java應用是很容易的 不過 世上沒有完全安全的系統 本文的加密方式提供了一定程度的源代碼保護 但對某些攻擊來說它是脆弱的
雖然應用本身經過了加密 但啟動程序DecryptStart沒有加密 攻擊者可以反編譯啟動程序並修改它 把解密後的類文件保存到磁碟 降低這種風險的辦法之一是對啟動程序進行高質量的模糊處理 或者 啟動程序也可以採用直接編譯成機器語言的代碼 使得啟動程序具有傳統執行文件格式的安全性
另外還要記住的是 大多數JVM本身並不安全 狡猾的黑客可能會修改JVM 從ClassLoader之外獲取解密後的代碼並保存到磁碟 從而繞過本文的加密技術 Java沒有為此提供真正有效的補救措施
lishixin/Article/program/Java/hx/201311/25751
2. 我需要 Bootstrap實戰,有這個書籍的百度網盤嗎
我這有資源 可以看下 Bootstrap實戰https://pan..com/s/1NMLjcSYAx5afhCHs9GSE9A?pwd=1234
本書由國內資深前端工程師撰寫,是目前內容最為全面和深入的Bootstrap專著。它不僅系統講解了Bootstrap的各項功能和使用方法,詳細講解了Bootstrap的組件、插件和擴展技術,而且深度解析了Bootstrap的內核源代碼。本書實戰性強,為各個知識點都精心設計了輔助說明問題的小案例,最後還包含一個綜合性的大案例,不僅能滿足讀者系統學習理論知識的需求,還能滿足讀者充分實踐的需求。