Java 原始碼的加密保護

Mondo 科技 更新 2024-02-16

如何在不修改原始程式的情況下通過加密保護原始碼**。

對於 C 或 C++ 等傳統語言,只要您不發布原始碼,就很容易在 Web 上保護原始碼。 不幸的是,j**a 程式的來源很容易被其他人窺視。 只要有反編譯器,任何人都可以分析別人的。 J**A 的靈活性使得原始碼很容易被竊取,但同時,它也使得通過加密來保護它相對容易,我們唯一需要知道的是 J**A 的類載入器物件。 當然,在加密過程中,有關 J**A 加密擴充套件 (JCE) 的知識也是必不可少的。

有幾種技術可以“混淆”j**a 檔案,使反編譯器在處理它們時效率降低。 但是,修改反編譯器來處理這些混淆的類檔案並不難,因此您不能簡單地依靠模糊測試來保證原始碼的安全。

我們可以使用流行的加密工具(例如 PGP(Pretty Good Privacy)或 GPG(GNU Privacy Guard))對應用程式進行加密。 此時,終端使用者必須在執行應用程式之前對其進行解密。 但是在解密後,終端使用者有乙個未加密的類檔案,這與事先不加密沒有什麼不同。

j**a 執行時隱式輸入位元組碼的機制意味著位元組碼是可以修改的。 每次 JVM 載入類檔案時,它都需要乙個名為 ClassLoader 的物件,該物件負責將新類載入到正在執行的 JVM 中。 jvm 為類裝入器提供了乙個檔案,其中包含要裝入的類(例如 j**a.)。lang.object),然後類載入器負責查詢類檔案,載入原始資料,並將其轉換為類物件。

我們可以自定義類載入器以在執行類檔案之前對其進行修改。 這種技術在這裡使用非常廣泛,其目的是在載入類檔案時對其進行解密,因此可以將其視為即時解密器。 由於解密的位元組碼檔案永遠不會儲存到檔案系統中,因此竊賊很難獲得解密的**。

由於將原始位元組碼轉換為類物件的過程完全由系統負責,因此建立自定義類載入器物件並不難,只要先獲取原始資料,然後可以進行包括解密在內的任何轉換。

J**A 2 在一定程度上簡化了自定義類載入器的構造。 在 J**A 2 中,LoadClass 的預設實現仍負責所有必需的步驟,但它也呼叫了乙個新的 FindClass 方法來適應各種自定義類載入過程。

這為我們提供了編寫自定義類載入器的快捷方式,省去了更少的麻煩:只需覆蓋 findclass,而不是 loadclass。 這種方法避免了重複載入器必須執行的所有常見步驟,因為這些步驟都由 loadclass 處理。

但是,本文中的自定義類裝入器不使用此方法。 原因很簡單。 如果預設類裝入器首先查詢加密的類檔案,則可以找到它; 但是,由於類檔案已加密,因此它將無法識別類檔案,並且載入過程將失敗。 因此,我們不得不自己實現 loadclass,這增加了一些工作量。

每個正在執行的 JVM 都已經有乙個類載入器。 此預設類裝入器根據類路徑環境變數的值在本地檔案系統中查詢相應的位元組碼檔案。

應用自定義類裝入器需要對過程有深入的了解。 我們首先必須建立乙個自定義類載入器類的例項,然後顯式要求它載入另乙個類。 這迫使 JVM 將類及其所需的所有類關聯到自定義類裝入器。 清單 1演示如何使用自定義 C lassloader 載入類檔案。

清單 1使用自定義類裝入器載入類檔案。

首先,建立乙個類載入器物件:classloader myclassloader = new myclassloader(); 使用自定義類載入器物件載入類檔案,並將其轉換為類物件類 myclass = myclassloaderloadclass( "mypackage.myclass" );最後,建立乙個類物件 newinstance = myclass 的例項newinstance();請注意,myclass 所需的所有其他類都將通過自定義類載入器自動載入
如前所述,自定義類裝入器只是從類檔案中獲取資料,並將位元組碼傳遞給執行時系統,其餘工作由執行時系統完成。

有幾種重要的方法可以執行類載入器。 在建立自定義類載入器時,我們只需要覆蓋其中乙個,即 loadclass,即可提供乙個 ** 來獲取原始類檔案資料。 此方法有兩個引數:類的名稱,以及指示 JVM 是否需要解析類名稱(即是否也載入依賴類)的標誌。 如果這個標誌是真的,我們只需要在返回 JVM 之前呼叫 resolveclass。

清單 2 classloader.loadClass()。

public class loadclass( string name, boolean resolve ) throws classnotfoundexception 所需的步驟 2:如果上述操作不成功,我們嘗試使用預設類載入器載入它 if (clasz == null) clasz = findsystemclass( name ); 必需步驟 3:如有必要,載入相關類 if (resolve &&clasz!= null) resolveclass( clasz );將類返回給呼叫方 return clasz;  }catch( ioexception ie ) catch( generalsecurityexception gse )
清單 2 顯示了乙個簡單的 loadclass 實現。 對於所有類裝入器物件,其中的大多數項都是相同的,但一小部分(標有注釋)是唯一的。 在處理過程中,還有其他幾種用於類裝入器物件的幫助程式方法:

findloadedclass:用於檢查請求的類當前是否存在。 loadclass 方法應首先呼叫它。 defineclass:獲取到原始類檔案的位元組碼資料後,呼叫defineclass將其轉換為類物件。 任何 loadclass 實現都必須呼叫此方法。 FindSystemClass:提供對預設類裝入器的支援。 如果用於查詢類的自定義方法找不到指定的類(或者您沒有故意使用自定義方法),則可以呼叫該方法來嘗試預設載入方法。 這很有用,尤其是在從普通 jar 檔案載入標準 j**a 類時。 ResolveClass:當 JVM 不僅要載入指定的類,還要載入該類引用的所有其他類時,它會將 loadclass 的 resolve 引數設定為 true。 在這種情況下,我們必須先呼叫 resolveclass,然後再將剛剛載入的類物件返回給呼叫方。 J**A 加密擴充套件 (JCE) 稱為 J**A 加密擴充套件。 它是SUN的加密服務軟體,包括加密和金鑰生成功能。 JCE 是 JCA(J**A 加密架構)的擴充套件。

JCE 不指定特定的加密演算法,但為可新增為服務提供程式的加密演算法的特定實現提供提供了乙個框架。 除了 JCE 框架之外,JCE 軟體包還包括 SunJCE 服務提供者,其中包括許多有用的加密演算法,例如 DES(Data Encryption Standard)和 Blowfish。

為簡單起見,在本文中,我們將使用 DES 演算法對位元組碼進行加密和解密。 以下是使用 JCE 加密和解密資料時必須遵循的基本步驟:

1. 生成安全金鑰。 在加密或解密任何資料之前,需要金鑰。 金鑰是與加密應用程式一起發布的一小段資料,清單 3 顯示了如何生成金鑰。

清單 3生成金鑰。

DES 演算法需要乙個可信的隨機數源,securerandom sr = new securerandom(); 為我們選擇的 DES 演算法生成乙個 keygenerator 物件:keygenerator kg = keygeneratorgetinstance( "des" );kg.init( sr );生成金鑰金鑰 = kggeneratekey();獲取金鑰資料位元組 rawkeydata = keygetencoded();* 然後你可以用金鑰加密或解密它,或者儲存為檔案以備後用 * dosomething(rawkeydata); 
2. 加密資料。 獲得金鑰後,您可以使用它來加密您的資料。 除了解密的類載入器之外,通常還有乙個單獨的程式來加密要發布的應用程式(參見清單 4)。

清單 4使用金鑰加密原始資料。

DES 演算法需要乙個可信的隨機數源,securerandom sr = new securerandom(); byte rawkeydata = * 以某種方式獲取金鑰資料 * ; 從原始金鑰資料建立乙個 DesKeySpec 物件 DesKeySpec dks = new deskeySpec(RawKeyData); 建立乙個金鑰工廠並使用它來將 deskeyspec 轉換為 secretkey 物件 secretkeyfactory keyfactory = secretkeyfactorygetinstance( "des" );secretkey key = keyfactory.generatesecret( dks );cipher 物件實際完成加密操作,cipher cipher = ciphergetinstance( "des" );使用金鑰初始化密碼物件密碼init( cipher.encrypt_mode, key, sr );現在,獲取資料並加密位元組資料 = * 以某種方式獲取資料 * 正式執行加密操作 byte encrypteddata = cipherdofinal( data );加密資料的進一步處理 dosomething( encrypteddata ); 
3. 解密資料。 執行加密應用程式時,類裝入器會分析並解密類檔案。 該過程如清單 5 所示。

清單 5使用金鑰解密資料。

DES 演算法需要乙個可信的隨機數源,securerandom sr = new securerandom(); byte rawkeydata = * 以某種方式獲取原始金鑰資料 * ; 從原始金鑰資料建立乙個 deskeyspec 物件 deskeyspec dks = new deskeyspec( rawkeydata ); 建立乙個金鑰工廠並使用它來將 deskeyspec 物件轉換為 secretkey 物件 secretkeyfactory keyfactory = secretkeyfactorygetinstance( "des" );secretkey key = keyfactory.generatesecret( dks );cipher 物件實際完成解密操作,cipher cipher = ciphergetinstance( "des" );使用金鑰初始化密碼物件密碼init( cipher.decrypt_mode, key, sr );現在,獲取資料和解密 byte encrypteddata = * 獲取加密資料 * 正式執行解密操作 byte decrypteddata = cipherdofinal( encrypteddata );解密資料的進一步處理 dosomething( decrypteddata ); 
前面我們介紹了如何加密和解密資料。 若要部署加密的應用,請按照下列步驟操作:

1. 建立應用程式。 我們的示例包含乙個主應用類和兩個幫助程式類(分別稱為 foo 和 bar)。 這個應用沒有做太多的實際工作,但只要我們可以加密這個應用,我們就可以加密其他應用。

2. 生成安全金鑰。 在命令列上,使用 generatekey 工具(請參閱 generatekeyj**a)將金鑰寫入檔案:

% j**a generatekey key.data
3.加密應用程式。 在命令列上,使用 encryptclasses 工具(請參閱 encryptclasses.)。j**a)加密應用程式的類:

% j**a encryptclasses key.data app.class foo.class bar.class
該命令將每個。 類檔案將替換為其各自的加密版本。

4. 執行加密的應用程式。 使用者通過 decryptstart 程式執行加密的應用程式。 decryptstart 過程如清單 6 所示。

清單 6 decryptstart.j**a,啟動加密應用程式的程式。

import j**a.io.*;import j**a.security.*;import j**a.lang.reflect.*;import j**ax.crypto.*;import j**ax.crypto.spec.*;公共類 DecryptStart 擴充套件了類載入器主過程:這是我們讀取金鑰並建立 DecryptStart 例項的地方,這是我們的自定義類載入器。 設定好類載入器後,我們用它來載入應用例項,最後通過 j**a 反射 API 呼叫應用例項的 main 方法 static public void main( string args throws exception ;  method main = clasz.getmethod( "main", mainargs );建立乙個包含 main() 方法物件引數的陣列 argsarray = ;  system.err.println( "[decryptstart: running "+appname+".main()]" );呼叫 main() maininvoke( null, argsarray );public class loadclass( string name, boolean resolve ) 丟擲 classNotFoundException }catch( fileNotFoundException fnfe ) 必需的第 2 步:如果上述操作不成功 我們嘗試使用預設的類載入器載入它 if (clasz == null) clasz = findsystemclass( name );必需步驟 3:如有必要,載入相關類 if (resolve &&clasz!= null) resolveclass( clasz );將類返回給呼叫方 return clasz;  }catch( ioexception ie ) catch( generalsecurityexception gse )
對於未加密的應用,正常執行如下:

% j**a app arg0 arg1 arg2
對於加密應用,相應的操作為:

% j**a decryptstart key.data app arg0 arg1 arg2
DecryptStart 有兩個用途。 decryptstart 的例項是實現即時解密的自定義類載入器; 同時,decryptstart 還包括乙個主程序,該程序建立解密器的例項並使用它來載入和執行應用程式。 示例應用包含在應用中j**a、foo.J**A 和 Barj**a. util.j**a 是乙個檔案 I o 工具,本文中的許多示例都使用了該工具。 如需完整**請從本文末尾**開始。

我們看到,在不修改原始碼的情況下加密 J**a 應用程式很容易。 但是,沒有完全安全的系統。 本文中的加密提供了一定程度的源保護,但它容易受到某些攻擊。

雖然應用本身是加密的,但啟動器 decryptstart 不是。 攻擊者可以反編譯啟動程式並對其進行修改,以將解密的類檔案儲存到磁碟。 降低這種風險的一種方法是以高質量混淆啟動過程。 或者,啟動程式可以直接編譯為機器語言,使啟動程式具有傳統可執行檔案格式的安全性。

同樣重要的是要記住,大多數 JVM 本質上並不安全。 狡猾的黑客可以修改 JVM,從類載入器外部獲取解密的 **,並將其儲存到磁碟,繞過本文的加密技術。 J**A沒有為此提供真正有效的補救措施。

但是,應該注意的是,所有這些可能的攻擊都有乙個前提,即攻擊者可以獲取金鑰。 如果沒有金鑰,應用程式的安全性完全取決於加密演算法的安全性。 雖然這種保護方法**並不完美,但它仍然是保護智財權和敏感使用者資料的有效方法。

相關問題答案

    常用的資料保護加密技術有哪些?

    常用的資料保護加密技術主要包括對稱加密技術和非對稱加密技術。對稱加密是指使用相同的金鑰進行加密和解密的過程。這種加密方法的安全性主要依賴於金鑰的保密性。對稱加密的常見演算法包括資料加密標準 DES 和高階加密標準 AES DES 是一種使用 位金鑰的對稱加密演算法,而 AES 是一種使用 位 位或 ...

    保護石墨換熱器應用

    保護石墨換熱器應用 .執行時,應防止溫度 壓力的突然變化,以及超溫 超壓現象的發生。當輸送介質的壓力超過裝置的工作壓力時,應在介質的入口處設定安全閥,以降低壓力。為防止蒸汽加熱時蒸汽的強烈衝擊,應緩慢開啟閥門,逐漸調節到最大或最小,在裝置執行過程中溫度緩慢公升高,當石墨零件溫度趨於均勻時,然後緩慢增...

    IP SSL證書網站加密保護,贏得搜尋引擎青睞!

    是的,IP SSL證書在加密保護和SEO優化方面有很長的路要走。以下是它們的主要影響 .加密保護。IP SSL證書可以從HTTP公升級為HTTPS,可以對其進行加密以保護使用者與使用者之間的資料傳輸。通過使用IP SSL證書,管理員可以確保使用者提供的敏感資訊 如登入憑據 個人資訊 支付資訊等 在傳...

    如何加密單詞?確保您的文件私密和安全

    Word文件在日常辦公中經常使用,那麼如何加密Word文件呢?以下是幾種常見的文件加密方法,希望對大家有所幫助。方法一 使用WPS對文件進行加密 選擇要加密的Word文件,然後單擊 檔案 選單。選擇 文件加密 輸入密碼,然後確認密碼。單擊 確定 按鈕完成加密操作。方法二 使用錦州資料夾加密大師 錦州...

    全球首創NFC加密支付技術問世

    近日,全球首由近場通訊 NFC 支援的加密貨幣支付技術。這一發展在加密貨幣領域是獨一無二的,為世界各地的商家和客戶開闢了新的可能性。創新突破 NFC加密 現在,付款過程幾乎與使用卡相同。使用 NFC 技術,只需輕觸使用者的手機,即可直接從使用者的加密錢包進行支付。忘記您的銀行卡 我們正在中斷支付!該...