Java Cryptography Architecture(JCA)——加密的那點兒事兒(加密服務提供者、算法名稱、轉換名稱)

在我們對數據進行安全加密和使用算法功能的時候,往往會碰到所謂的:“加密服務提供者”——即:Provider,“所請求算法的名稱”——即:algorithm,“轉換名稱”——即:transformation(如:AES/ECB/PKCS7Padding)等這樣的字眼,很多同學都對這模棱兩可,也懶得看官網上的源碼,今天我就和大家分享一下ORACLE官網上是怎麼說的。

這裏給出幾個API,和oracle官網的JAC文檔,想更詳細瞭解的可以自己看看,如果發現本人理解有偏差,請留言,多謝!

Provider —— http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/security/Provider.html

Security —— http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/security/Security.html

JAC —— http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html

建議用Google瀏覽器看,Google自帶翻譯功能,有助於閱讀。

一、Java的加密體系結構——JCA

首先說一下JCA(Java Cryptography Architecture),叫Java加密體系結構,是Java平臺的重要組成部分。Java平臺強調安全性,包括語言安全、密碼學、公鑰基礎設施、認證、安全通信和訪問控制等,而JCA可以輕鬆地將安全功能集成到我們的應用程序中。在JDK 1.4之前,JCE是一個非捆綁產品,因此,JCA和JCE經常被稱爲單獨的、獨特的組件。由於JCE現在已經被JDK捆綁在一起,這個區別變得越來越不明顯。由於JCE使用與JCA相同的架構,所以JCE應該更適當地被認爲是JCA的一部分。

(1)JCA圍繞這些原則設計:

實施獨立性和互操作性
算法獨立性和可擴展性
實現獨立性和算法獨立性是互補的;您可以使用加密服務,如數字簽名和消息摘要,而不必擔心實現細節,甚至是形成這些概念基礎的算法。儘管無法完成算法獨立性,但JCA提供了標準化的,特定於算法的API。當不實現獨立性時,JCA允許開發人員指定具體的實現。
通過定義加密“引擎”(服務)的類型以及定義提供這些加密引擎的功能的類來實現算法獨立性。這些類被稱爲引擎類,比如有: MessageDigest,Signature,KeyFactory,KeyPairGenerator,和Cipher等類。
使用基於“提供者”的體系結構實現獨立性。術語加密服務提供商(CSP)(與本文檔中的“提供者”互換使用)是指實現一個或多個加密服務的包或一組包,例如數字簽名算法,消息摘要算法和密鑰轉換服務。程序可以簡單地請求Signature實現特定服務(例如DSA簽名算法)的特定類型的對象(如:Signature對象),並從安裝的提供者之一獲得實現。如果需要,程序可以替代地從特定提供商請求實現。提供商可能會透明地更新應用程序,例如,當有更快或更安全的版本可用時。
實現互操作性意味着各種實現可以彼此工作,使用對方的密鑰或驗證彼此的簽名。這意味着例如對於相同的算法,由一個提供商產生的密鑰將被另一個提供者使用,並且由一個提供商生成的簽名將被另一個提供商驗證。
算法可擴展性意味着可以輕鬆添加適合其中一個受支持引擎類的新算法。

(2)JDK中的JCA包括兩個軟件組件:

1、該框架定義和支持供應商提供實施的加密服務。該框架包括的包有java.security、javax.crypto、javax.crypto.spec和 javax.crypto.interfaces。這些包爲Java中的安全框架、cryptographic(加密)操作、密鑰規範和算法參數規範提供類和接口。

2、實際的加密服務供應商(即:Provider),如Sun, SunRsaSign,SunJCE等,其中包含實際的加密的實現。

每當提到特定的JCA提供者時(即:Provider),將通過提供者的名稱來明確地引用該提供者。

(3)如何使用JCA

要使用JCA,應用程序只需要一個特定類型的對象(例如:MessageDigest)和特定的算法或服務(例如:“MD5”算法),並從一個已安裝的提供程序獲取實現,或者,程序可以從特定提供者請求對象。每個提供者都有一個名稱用於引用它。

md = MessageDigest.getInstance(“MD5”); 

md = MessageDigest.getInstance(“MD5”,“ProviderC”);

下圖說明了請求“MD5”消息摘要實現。該圖顯示了實現各種消息摘要算法(“SHA-1”,“MD5”,“SHA-256”和“SHA-512”)的三個不同提供者。提供者從左到右按優先順序排列(1-3)。在第一個圖示中,應用程序請求MD5算法實現而不指定提供者名稱。以優先順序搜索提供者,並返回提供該特定算法ProviderB的第一個提供者的實現。在第二個圖中,應用程序從特定提供者 ProviderC 請求MD5算法實現。這次從ProviderC的實現被返回。


在Sun JDK加密實現經由若干不同的供應商分佈(Sun, SunJSSE,SunJCE,SunRsaSign)主要由於歷史原因,但通過的功能以及他們提供的算法的類型程度較輕。其他Java運行時環境可能不一定包含這些Sun提供程序,因此除非知道特定提供程序可用,否則應用程序不應請求提供者特定的實現。

 

二、安全API——Java的核心API

安全API(The Security API),它是Java編程語言的核心API,包含在java.security包(及其子包)中。該API的目的是允許開發人員將不管是低級別還是高級別的安全功能併入他們的程序中。Security是包java.security下的一個類(包的地址爲:%JAVA_HOME%\jre\lib\security\java.security),此類集中了所有的安全屬性和常見的安全方法,其主要用途之一是管理提供程序。它只包含靜態方法,從不實例化,添加或刪除提供者以及設置Security屬性的方法只能由受信任的程序執行。

目前,“可信程序”包括:

1、一個本地應用程序不在安全管理器下運行

2、具有許可執行指定方法的小程序或應用程序。

可信的的程序要執行一個操作(例如添加提供程序),需要該小程序被授予該特定操作的適當權限。JDK安裝的策略配置文件從指定代碼源的代碼能得到一些權限(系統資源的訪問類型)。(有關詳細信息,請參閱下文以及 “默認策略實施和策略文件語法”和“Java安全體系結構規範”文件。)

       既然Security類主要用途之一是管理提供者,那就來總結了 Security類中可用於查詢哪些 Provider安裝的方法,以及在運行時安裝或刪除提供程序的方法。

1、獲得Provider(提供者)

static Provider[] getProviders();返回一個包含所有已安裝提供程序的數組(從技術上講,Provider每個程序包提供程序的子類)。Providers在數組中的順序是它們的優先順序。

static Provider getProvider(String providerName);返回以providerName命名的Provider。如果 Provider沒有找到,則返回null。

2、添加Provider(提供者)

static int addProvider(Provider provider);在已安裝的Providers的列表的末尾添加provider,並返回其添加的位置座標,如果它已被安裝,則返回-1。

static int insertProviderAt(Provider provider, int position);在指定位置position處添加提供者provider,並且將position位置及大於position位置的所有Provider都向上移動一個位置(朝向已安裝提供程序列表的尾部)。此方法返回添加的provider位置座標,如果它已被安裝,則返回-1。

 3、刪除Provider(提供者)

static void removeProvider(String name);刪除指定名字爲name的Provider。當指定的提供者被刪除時,位於大於指定Provider位置的所有Provider都向下移動一個位置(朝向已安裝提供程序列表的頭部)。

注意:如果要更改提供者的位置,則必須先刪除它,然後將其插入新的位置。

             

三、提供者Provider

Provider是java.security包下的一個類。此類表示 Java 安全 API "provider",這裏 provider 實現了 Java 安全性的一部分或者全部。“Cryptographic Service Provider”(即:加密服務提供者,與本文的Provider可以互換使用)是指提供JDK Security API加密功能子集的具體實現的軟件包或一組軟件包,而Provider類正是這樣的包或包集的接口。java.security.Provider是所有安全提供商的基類,它具有訪問提供商名稱,版本號和其他信息的方法。請注意,除了註冊加密服務的實現之外, Provider類還可以用於註冊可能被定義爲JDK Security API或其擴展之一的其他安全服務的實現。

(1)provider 可以實現的服務包括:

1、算法(如 DSA、RSA、MD5 或 SHA-1)。

2、密鑰的生成、轉換和管理設施(如用於特定於算法的密鑰)。

每個 provider 有一個名稱和一個版本號,並且在每個它裝入運行時中進行配置。

有關特定類型的provider、加密服務 provider 如何工作和安裝的信息,請參閱 "Java Cryptography Architecture API Specification &Reference" 中的 The Provider Class。但是,請注意 provider 能夠被用來實現 Java 中的任何安全服務,這些安全服務使用帶有適合下層的實現選擇的可插入架構。

(2)請求和提供提供者的幾種方式

對於API中的每個引擎類,通過調用引擎類中的一個方法getInstance來請求和實例化,可以隨意指定所需算法的名稱,期望的提供者的名稱(或類)

static EngineClassNamegetInstance(String algorithm) throws NoSuchAlgorithmException

static EngineClassNamegetInstance(String algorithm,String provider) throwsNoSuchAlgorithmException,NoSuchProviderException

static EngineClassNamegetInstance(String algorithm,Provider provider) throwsNoSuchAlgorithmException

其中EngineClassName是所需的引擎類型(MessageDigest /Cipher / etc)。例如:

MessageDigest md =MessageDigest.getInstance(“MD5”);

KeyAgreement ka = KeyAgreement.getInstance(“DH”,“SunJCE”);

分別返回“MD5”下的MessageDigest和“DH”下的KeyAgreement對象的實例。

注意:算法名稱不區分大小寫。例如,以下所有調用都是等價的:

MessageDigest.getInstance(“SHA-1”)

MessageDigest.getInstance(“sha-1”)

MessageDigest.getInstance(“sHa-1”)

一些提供者也可以選擇指代相同算法的別名。例如,“SHA-1”算法可能被稱爲“SHA1”。但是,應用程序應使用標準名稱而不是別名,因爲並非所有提供者可能以相同的方式將算法名稱進行別名。

如果沒有指定提供者,則getInstance會搜索已註冊的提供者實現與命名算法相關聯的所請求的加密服務。在任何給定的Java虛擬機(JVM)中,提供者按照給定的優先順序進行安裝,如果沒有請求特定的提供者,則會按提供者列表的順序搜索。例如:假設有安裝在JVM兩家供應商PROVIDER_1和PROVIDER_2

PROVIDER_1實現SHA1withDSA,SHA-1,MD5,DES和DES3。

PROVIDER_1具有優先級1(最高優先級)。

PROVIDER_2實現SHA1withDSA,MD5,RSA,MD2,RSA,MD2,MD5,RC4,RC5,DES和RSA。

PROVIDER_2有優先級2。

現在來看看三種情況:

1.如果我們正在尋找一個MD5實現。兩個提供商都提供這樣的實現。在PROVIDER_1 返回的實現,因爲PROVIDER_1具有最高優先級,並首先搜索。

2.如果我們正在尋找一個MD5withRSA簽名算法, PROVIDER_1首先要搜索它。沒有找到實現,所以PROVIDER_2被搜索。由於找到了一個實現,它被返回。

3.假設我們正在尋找一個SHA1withRSA簽名算法。由於沒有安裝的提供程序實現它,所以拋出一個NoSuchAlgorithmException。

提供者中的getInstance方法是爲了給那些想指定算法提供者的開發人員的。例如,聯邦機構將希望使用已獲得聯邦認證的提供商實施。我們假設SHA1withDSA實現從PROVIDER_1沒有收到這樣的認證,而DSA的實現PROVIDER_2已經收到了。

然後聯邦機構計劃將進行以下呼籲,具體說明PROVIDER_2該計劃具有認證實施:

簽名dsa = Signature.getInstance(“SHA1withDSA”,“PROVIDER_2”);

在這種情況下,如果PROVIDER_2未安裝,即使另一個已安裝的提供程序實現了請求的算法,還是會拋出一個NoSuchProviderException。

程序還可以選擇獲取所有已安裝的提供程序的列表(使用Security類中的getProviders方法)並從列表中選擇一個。

注意:通用應用程序不應要求特定提供商提供加密服務。否則,應用程序與其他Java實現可能不可用的特定提供程序相關聯。他們還可能無法利用具有比特定請求的提供商更高的優先級順序的可用的優化提供者(例如通過PKCS11的硬件加速器或諸如Microsoft的MSCAPI的本地操作系統實現)。

每個Provider類實例都有一個(當前是區分大小寫的)名稱,一個版本號和一個提供者及其服務的字符串描述。你可以通過調用Provider的以下方法來查詢實例的信息:

public String getName()

public double getVersion()

public String getInfo()


四、實例

根據上面的講解,我們現在開始動手得到我們想要的。

(1)得到Security中包含的提供者

在main方法中寫入以下代碼,就可打印出Security中所有的提供者(包括每個提供者的名字、版本、詳情)

Provider[] arr = Security.getProviders();
		for (int i = 0; i < arr.length; i++) {
			System.out.println("名字:" + arr[i].getName() + "\n版本:" + arr[i].getVersion() + "\n詳情:" + arr[i].getInfo() + "\n");
		}

其打印結果樣式爲(因提供者比較多,就不一一列舉,想看的同鞋,可以自己打印看看):

名字:SUN
版本:1.8
詳情:SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS & DKS keystores; PKIX CertPathValidator; PKIX CertPathBuilder; LDAP, Collection CertStores, JavaPolicy Policy; JavaLoginConfig Configuration)

(2)得到提供者中的所有屬性名字及其對應的值

在(1)中我們可以得到存儲提供者的集合Provider[],根據Provider的API我們可看到其中有一個方法entrySet(),其返回值爲Set<Map.Entry<Object,Object>>,調用該方法後返回此 Provider 中所包含的屬性項,說明Provider的屬性是以鍵值對的方式存儲。這樣我們就可以打印處其中的屬性並找到算法了。在main方法中寫如下代碼:
Provider[] arr = Security.getProviders();
		for (int i = 0; i < arr.length; i++) {
			Set keys = arr[i].keySet();
            for (Iterator it=keys.iterator(); it.hasNext(); ) {
                String key = (String)it.next();
                // 每個keys的屬性項可能由一個字符串組成,或是有一個空格分隔的字符串組成,帶空格的字符串有兩種情況(命名:空格前爲屬性項,空格後爲屬性名)
                // 1、相同屬性項下的不同屬性名(如:“Provider.id name” 和 “Provider.id version”)
                // 2、不同屬性項下的相同屬性名(如:“TransformService.http://www.w3.org/2001/10/xml-exc-c14n# MechanismType” 和 “TransformService.http://www.w3.org/TR/1999/REC-xpath-19991116 MechanismType”)
                key = key.split(" ")[0];
                System.out.println("key : " + key + "\nvalue:" + arr[i].get(key) + "\n");
            }
		}
因其打印結果太長,我只截取一部分,想看的筒靴可以自己打印一下,看有多少種算法:

key : Alg.Alias.Signature.SHA1/DSA
value:SHA1withDSA

key : Alg.Alias.Signature.1.2.840.10040.4.3
value:SHA1withDSA

key : Alg.Alias.Signature.DSS
value:SHA1withDSA

…………

key : Signature.SHA512withRSA
value:sun.security.mscapi.RSASignature$SHA512

key : Signature.SHA256withRSA
value:sun.security.mscapi.RSASignature$SHA256

key : KeyStore.Windows-MY
value:sun.security.mscapi.KeyStore$MY

注意:打印出來的包含所有的provide的所有屬性,裏面包括版本號、提供者名字、算法名字、機制類型、密鑰的轉換服務等等各種屬性及其對應的value,所以我們想要得到我們想要的算法還得再從中查找。

(3)得到提供者中某種算法的名字

看到上面的結果,是否還是有些頭疼呢,代碼中看見寫的算法名都是“AES”,“MD5”什麼的,這名字相差甚遠,好了,現在我們就來得到我們想要的,直接上代碼:
/**
	 * 我們就一摘要算法爲例,如果感興趣的筒靴,還可以試試“Cipher”,或者是上文所提到的“引擎”類試試。
	 * 
	 * @param serviceType
	 * @return
	 */
	public static String[] getCryptoImpls(String serviceType) {
        Set result = new HashSet();
    
        // 得到所有提供者
        Provider[] providers = Security.getProviders();
        for (int i=0; i<providers.length; i++) {
            // 獲得每個提供者提供的服務
            Set keys = providers[i].keySet();
            // 遍歷keys
            for (Iterator it=keys.iterator(); it.hasNext(); ) {
                String key = (String)it.next();
                key = key.split(" ")[0];
                // 找到我們想要的算法,並將想要的算法的名字擇出來
                if (key.startsWith(serviceType+".")) {
                    result.add(key.substring(serviceType.length()+1));
                } else if (key.startsWith("Alg.Alias."+serviceType+".")) {
                    // This is an alias
                    result.add(key.substring(serviceType.length()+11));
                }
            }
        }
        return (String[])result.toArray(new String[result.size()]);
    }
然後,我們在主函數中調用該方法,並傳入我們想傳的算法“MessageDigest”
public static void main(String[] args) {
		String[] re = getCryptoImpls("MessageDigest");
		for (int i = 0; i < re.length; i++) {
			System.out.println(re[i]);
		}
	}
然後我們看看打印結果:

SHA-1
SHA1
SHA-384
OID.1.3.14.3.2.26
2.16.840.1.101.3.4.2.2
SHA
2.16.840.1.101.3.4.2.1
2.16.840.1.101.3.4.2.4
2.16.840.1.101.3.4.2.3
OID.2.16.840.1.101.3.4.2.4
OID.2.16.840.1.101.3.4.2.3
OID.2.16.840.1.101.3.4.2.2
1.3.14.3.2.26
OID.2.16.840.1.101.3.4.2.1
SHA-224
SHA-256
MD2
SHA-512
MD5

有沒有很熟悉!相信那些字母的字符串都不陌生,但那些純數字和點的字符串是什麼呢?這些其實也是算法的名字,在其對應的value值中就顯示廬山真面目了
(如:key:Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3——value:SHA-512)
眼尖的筒靴很快發現,下面還有一個SHA-512,這不是重複了嗎?別忘了,我們檢索的是所有提供者的算法,根據上文的Provider中介紹可知,不同的提供者可以有相同的命名算法。

至於“轉換名稱”,感興趣的童協可以自行搜索一下,日後有時間我找找資料看看,再寫,請見諒!
記得有什麼問題請留言!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章