一、JCA/ JCE
-
JCA(Java Cryptography Architecture) 是Java體系結構,提供了基本Java加密框架,比如證書、數字簽名、消息摘要、祕鑰對生成器等,在java.security包中實現。
-
JCE(Java Cryptography Extension)是JCA的擴展,主要負責提供DES、AES、RSA、DSA這樣的加密算法,因爲加密算法是會不斷進步的,會有新的算法誕生,所以整個安全體系結構的可擴展性必須要得到保證。
-
JCE包因爲其加密算法的安全限制,受美國出口限制,我們平時使用的是oracle提供的“閹割版”,比如默認不允許256位密鑰的AES,我們通過oracle官網,可以下載“完整版”。
下面是“完整版的加密擴展包”
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html -
解壓縮以後是這個樣子:
完整版加密擴展包.png
根據readme中的提示,放在<java-home>/lib/security包中就可以啦
image.png
-
JCE並不是只有oracle提供的,有多家廠商可以提供JCE的擴展包,在我們jdk的安裝目錄下的java.security文件中可以看到,支持的服務提供者Provider。
Provider
-
JCA的實現是在java.security包下,這個包只能實現消息摘要算法,其他都要依賴JCE擴展包,在javax.crypto包中實現,也可以添加自己的provider,根據上圖的格式繼續填寫第11個即可。使用Security.addProvider("xxx")可以完成Provider的添加,使用insertProviderAt("xxx", position)可以在指定位置完成添加,這裏涉及到一個優先級的概念,如上圖Provider所示,數字越小優先級越高。
-
借用oracle官方的一個例子,如圖privider example,我們可以使用如下代碼獲得摘要實例,比如我們要獲取SHA-256算法生成摘要的實例,第一句代碼會走左側的圖,遍歷所有provider,獲取到支持該算法的Provider就立刻返回,左側會返回ProviderB,而生成摘要是提供重載方法的,可以指定Provider,第二行代碼指定了ProviderC,那麼就會直接返回ProviderC,可以看到使用方法簡單靈活。
privider example
md = MessageDigest.getInstance("SHA-256");
md = MessageDigest.getInstance("SHA-256", "ProviderC");
-
官方是不推薦使用Sun, SunJSSE,SunJCE,SunRsaSign這些廠商提供的Provider的,據說是因爲歷史原因導致這些廠商提供的功能和加密強度都不怎麼樣 。
-
MessageDigest.getInstance("SHA-256");這一行代碼,的執行流程會是什麼樣子的呢?我們找到MessageDigest在包中位置,發現旁邊還有一個和他的名字很像的SPI類,SPI全稱是Service Provider Interface,是軟件設計可插拔的體現,經常被用在插件的設計上。
-
類似MessageDigest這樣的類,我們通常叫做“engine”類,可以翻譯叫做引擎類,每一個這樣的引擎類都有一個對應的SPI類,引擎類繼承SPI類,引擎類負責被用戶調用,引擎類調用SPI類,我們發現所有的SPI類都是abstract的,也就是說SPI類提供模板,其他Provider實現SPI類中的方法就可以了,這些對於用戶來說都是透明的,用戶只需要在java.security中進行配置就可以了,是可插拔的一種體現,後面將會對這些引擎類進行簡單介紹。
MessageDigestSpi
-
對於某些provider,可能會提供加密算法的別名,官方文檔不推薦使用別名這種行爲,因爲其他廠商不一定叫這個別名。
-
如果想知道自己可以使用那些Provider,Security的getProvider()方法可以獲取到,獲取到key有很多,筆者使用的是1.8版本,有600+條,如下面這段代碼。
public static void main(String[] args) { for (Provider p : Security.getProviders()) { System.out.println(p); for (Map.Entry entry :p.entrySet()) { System.out.println("\t"+entry.getKey()); } } }
二、java.security包類庫
- 類似MessageDigest這樣的類,我們通常叫做引擎類,java.包中的引擎類都是爲其繼承的SPI類服務,這樣的可插拔的機制方便了整體的擴展,下面將介紹java.security包下的引擎類。
1、MessageDigest類
-
可以使用MessageDigest生成指定加密算法的摘要,代碼如下,首先根據MessageDigest的靜態方法獲取對應算法的實例,然後調用update()方法更新摘要,最後使用digest()方法生成摘要。
MessageDigest digest = MessageDigest.getInstance("SHA"); digest.update(input); byte[] output = digest.digest();
2、Key接口
-
Key是一個接口,是祕鑰的抽象概念,所有與祕鑰相關的類都要實現這個接口,通過查看源代碼我們可以發現這個接口繼承了Serializable接口,這是因爲祕鑰多數情況都是在系統間進行傳輸,這個接口很簡單隻有三個方法,如下:
public String getAlgorithm();//獲取算法名稱
public String getFormat();//獲取祕鑰格式化的編碼格式
public byte[] getEncoded();//返回二進制格式的祕鑰 -
有三個接口繼承了它:分別是SecretKey、PublicKey、PrivateKey,SecretKey是對稱祕鑰的抽象,對稱加密算法的祕鑰由SecretKey提供,PublicKey和PrivateKey對應非對稱件加密算法中的公鑰和私鑰。
3、KeyPair接口
- KeyPair是非對稱加密算法祕鑰對的抽象,如下圖所示,這個類的構造方法參數只有兩個,類型爲PublicKey和PrivateKey,這個類是不提供公鑰和私鑰的setter方法的,只可以通過構造方法的方式去構造祕鑰對。
KeyPair
4、KeyPairGenerator類
-
祕鑰對的生成就是由KeyPairGenerator來完成的,這是一個引擎類,同樣也繼承了SPI類,通過工廠方法getInstance()來完成祕鑰對的構建,下面通過調用KeyPairGenerator的getInstance()方法獲得實例,初始化祕鑰長度,然後獲得祕鑰對。
KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA"); generator.initialize(1024); KeyPair keys = generator.generateKeyPair();
5、KeyFactory類
-
KeyFactory最大的作用就是根據指定的規範生成祕鑰,如下面這段代碼,使用的是PKCS8規範,這樣的規範還有很多,如下圖所示:
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
spec
6、Signature類
-
Signature類主要用於生成和驗證數字簽名,生成數字簽名這一步我們通常稱爲“加簽”,驗證數字簽名這一步,我們通常稱爲“驗籤”,首先我們根據KeyPairGenerator的工廠方法getInstance()獲取實例祕鑰對實例,初始化祕鑰長度1024,生成祕鑰對,根據指定算法構建Signature簽名實例,根據私鑰初始化,調用sign()方法完成加簽,下面這一段代碼展示了加簽的過程:
byte[] data = "Signature Data".getBytes(); KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA"); generator.initialize(1024); KeyPair keyPair = generator.generateKeyPair(); Signature signature = Signature.getInstance(generator.getAlgorithm()); signature.initSign(keyPair.getPrivate()); signature.update(data); byte[] signData = signature.sign();
-
驗簽過程和加簽相似,使用公鑰初始化,調用verify()方法完成驗籤,如下面代碼所示:
signature.initVerify(keyPair.getPublic()); signature.update(data); boolean status = signature.verify(signData);
7、KeyStore類
-
KeyStore是祕鑰庫的抽象,用於管理祕鑰和證書,這也是一個引擎類,如下代碼,我們從本地加載祕鑰庫配合密碼加載祕鑰庫,這裏我們可以看到通過keyStore.aliases();獲取了全部的別名列表,原因是keystore是通過別名去加載祕鑰的,原理類似前面提到的Provider,獲取到第一個別名返回。
String keyPassword = "123456"; inputStream = new FileInputStream(new File(keyPath)); KeyStore keyStore = KeyStore.getInstance(keyType); keyStore.load(inputStream, keyPassword.toCharArray()); Enumeration enum2 = keyStore.aliases(); String keyAlias = null; if (enum2 == null) { return null; } else { if (enum2.hasMoreElements()) { keyAlias = (String) enum2.nextElement(); } else { return null; } } PrivateKey priviteKey = keyStore.getKey(keyAlias, keyPassword.toCharArray());