Java安全——金鑰那些事
標籤(空格分隔): Java 安全
概念
金鑰是加密演算法不可缺少的部分。金鑰在安全體系中至關重要,正如其名,私密的鑰匙,開啟安全的大門。金鑰分兩種:對稱金鑰和非對稱金鑰。非對稱金鑰裡又包含公開金鑰和私有金鑰。
與金鑰相關的還有一個概念是證照。證照主要用於鑑別金鑰,通常將公開金鑰放到證照裡傳輸。
Java的安全體系裡,金鑰是通過JCE演算法包實現的。操作金鑰的引擎包含兩部分:金鑰生成器和金鑰工廠。金鑰生成器可以建立金鑰,而金鑰工廠將其進行包裝展示到外部。所以對於編寫程式來說,建立金鑰包括兩個步驟:1,用金鑰生成器產生金鑰;2,用金鑰工廠將其輸出為一個金鑰規範或者一組位元組碼。
Java實現
Java裡將金鑰封裝了一個介面——Key。非對稱金鑰有PublicKey和PrivateKey,均實現了該介面。從之前的“安全提供者框架”中的輸出結果可以看到,不同的安全提供者提供了很多金鑰生成演算法,比較典型的是Sun的DSA和RSA以及JCE的Diffie-Hellman演算法。
生成和表示key
金鑰的生成,Java提供了兩個生成器類——KeyPairGenerator和KeyGenerator,前者用於生成非對稱金鑰,後者用於生成對稱金鑰。對應金鑰的表示,KeyFactory類表示非對稱金鑰,SecretKeyFactory表示對稱金鑰。
我們來看一個DSA的例子:
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class KeyTest {
public static void main(String[] args) {
try {
generateKeyPair();
generateKey();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public static void generateKeyPair() throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(512);
KeyPair kp = kpg.generateKeyPair();
System.out.println(kpg.getProvider());
System.out.println(kpg.getAlgorithm());
KeyFactory kf = KeyFactory.getInstance("DSA");
DSAPrivateKeySpec dsaPKS = kf.getKeySpec(kp.getPrivate(), DSAPrivateKeySpec.class);
System.out.println(" DSA param G:" + dsaPKS.getG());
System.out.println(" DSA param P:" + dsaPKS.getP());
System.out.println(" DSA param Q:" + dsaPKS.getQ());
System.out.println(" DSA param X:" + dsaPKS.getX());
}
public static void generateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyGenerator kg = KeyGenerator.getInstance("DES");
SecretKey key = kg.generateKey();
System.out.println(kg.getProvider());
System.out.println(kg.getAlgorithm());
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
DESKeySpec desKS = (DESKeySpec) skf.getKeySpec(key, DESKeySpec.class);
System.out.println(" DES key bytes size:" + desKS.getKey().length);
}
}
Key生成的程式碼架構設計類圖如下:
KeyGenerator與KPG類似,只是KPG生成KeyPair,KG
生成SecretKey。
金鑰管理
關於證照
證照這東西,真不知道放哪裡合適,就不單獨拿出來講了。考慮到證照可以驗證金鑰的合法性,就放這裡說一下吧。
因為非對稱金鑰的場景,需要將公鑰傳輸給對應的需求者。那麼如何傳輸能確保這個公鑰是我給你的而不是別人替換的呢?那就需要對這次傳輸加密簽名,於是又進入了這樣的迴圈。於是就引出了證照——證照可以保證內容和發源地是一致的,也就是說證照可以保證發給需求者的內容確實是屬於內容擁有者的。
證照不是誰都能來發的,證照是通過一個公正實體(CA,證照授權機構)來頒發並驗證合法性。證照包含三方面內容:
1,實體名,即證照持有者。
2,與主體相關的公開金鑰。
3,驗證證照資訊的數字簽名。證照由證照發行人簽名。
Java中有對應的Certificate類來做證照相關的事情。但是證照不是我們這裡要討論的重點,而且Java本身對證照的支援就不完備。因此證照的內容就在這裡插播一下。我們還是回到金鑰的傳輸問題上來。
KeyStore
Java中KeyStore類負責金鑰的管理,KeyStore有個setKeyEntry()方法。通用的流程是KeyStore將key設定為一個key entry。然後通過store()方法儲存為.keystore檔案。使用方得到.keystore檔案,利用load()方法讀取Key entry,然後使用。
如果是非對稱金鑰的祕密金鑰,寫入金鑰項的使用方法如下:
public static void secretKeyStore() throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
char[] password = "123456".toCharArray();
String fileName = System.getProperty("user.home") + File.separator + ".keystore";
FileInputStream fis = new FileInputStream(fileName);
KeyStore ks = KeyStore.getInstance("jceks");
ks.load(fis, password);
KeyGenerator kg = KeyGenerator.getInstance("DES");
SecretKey key = kg.generateKey();
ks.setKeyEntry("myKeyEntry", key, password, null);
FileOutputStream fos = new FileOutputStream(fileName);
ks.store(fos, password);
System.out.println("store key in " + fileName);
}
這裡帶來一些概念:
- 金鑰庫:也就是上面說的KeyStore,用來管理存放金鑰和證照的地方。Java的金鑰管理是基於金鑰庫來構建的。
- 金鑰項:金鑰庫裡存放的是一條條的金鑰項。金鑰項要麼儲存一個非對稱金鑰對,要麼儲存一個祕密金鑰。如果儲存的是金鑰對,那還可能儲存一個證照鏈。證照鏈的第一個證照包含公鑰。
- 別名:每個金鑰都會可以有個別名,可以理解為金鑰項的名字。
-
標識名:金鑰庫中的實體的標識名是其完整的X.500名的子集,比如一個DN是
CN=Yu Jia, OU=ALI, O=ALIBABA, L=HZ, ST=ZJ, C=CN
- 證照項:只包含一個公鑰證照,儲存的是證照而不是證照鏈。
- JKS,JCEKS,PKCS12:金鑰庫演算法,Java預設是JKS,只能儲存私鑰,要想儲存對稱金鑰的祕密金鑰,需要使用JCEKS,這也就是上面程式碼中提到的
KeyStore ks = KeyStore.getInstance("jceks");
。可以通過修改java.security檔案中的keystore.type=JCEKS來更改預設演算法。
Keytool
光是這樣,還欠點什麼,因為上面的程式碼放到main函式裡還是無法執行,而且也有個疑問,明明是要建立keystore,幹嘛還要先load?
看看KeyStore中store()方法的原始碼:
public final void store(OutputStream stream, char[] password)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException
{
if (!initialized) {
throw new KeyStoreException("Uninitialized keystore");
}
keyStoreSpi.engineStore(stream, password);
}
未初始化的Keystore是要丟擲KeyStoreException的。而初始化動作是在load()方法裡做的。那這就奇怪了,第一個keystore難道是自己隨便在系統目錄裡touch的?
這就引出了keytool工具,這是一個JRE提供的管理工具,方便管理金鑰庫的。keytool是命令列介面,使用keytool命令可以管理金鑰庫,具體命令各個引數可以man keytool或者keytool -help瞭解。
我這裡列出我的程式是如何初始化一個keystore的:
1,我先生成了一個別名叫做changedi的金鑰項,其演算法是RSA非對稱演算法
zunyuanjys-MacBook-Air:~ zunyuan.jy$ keytool -genkey -alias changedi -keyalg RSA
輸入金鑰庫口令:
再次輸入新口令:
您的名字與姓氏是什麼?
[Unknown]: Yu Jia
您的組織單位名稱是什麼?
[Unknown]: ALI
您的組織名稱是什麼?
[Unknown]: ALIBABA
您所在的城市或區域名稱是什麼?
[Unknown]: HZ
您所在的省/市/自治區名稱是什麼?
[Unknown]: ZJ
該單位的雙字母國家/地區程式碼是什麼?
[Unknown]: CN
CN=Yu Jia, OU=ALI, O=ALIBABA, L=HZ, ST=ZJ, C=CN是否正確?
[否]: Y
輸入 <changedi> 的金鑰口令
(如果和金鑰庫口令相同, 按回車):
再次輸入新口令:
2,依照提示輸入完成DN後,keystore就建立好了,可以檢視一下
zunyuanjys-MacBook-Air:~ zunyuan.jy$ keytool -list
輸入金鑰庫口令:
金鑰庫型別: JKS
金鑰庫提供方: SUN
您的金鑰庫包含 1 個條目
changedi, 2016-7-7, PrivateKeyEntry,
證照指紋 (SHA1): 76:C8:CE:EA:4C:29:6D:0E:FF:8C:02:BE:F4:F4:55:97:63:1F:C8:26
3,可以看到,這個庫還是JKS的,需要更改為JCEKS,於是做下面的事
zunyuanjys-MacBook-Air:~ zunyuan.jy$ keytool -keypasswd -alias changedi -storetype jceks
輸入金鑰庫口令:
輸入 <changedi> 的金鑰口令
新<changedi> 的金鑰口令:
重新輸入新<changedi> 的金鑰口令:
4,再list時,要選擇storetype,因為剛才雖然是修改密碼,但是其實核心目的是要更改金鑰庫型別
zunyuanjys-MacBook-Air:~ zunyuan.jy$ keytool -list -storetype jceks
輸入金鑰庫口令:
金鑰庫型別: JCEKS
金鑰庫提供方: SunJCE
您的金鑰庫包含 1 個條目
changedi, 2016-7-7, PrivateKeyEntry,
證照指紋 (SHA1): 76:C8:CE:EA:4C:29:6D:0E:FF:8C:02:BE:F4:F4:55:97:63:1F:C8:26
5,執行剛才的程式,寫一個對稱金鑰的祕密金鑰進去,作為這個keystore的一個金鑰項,再list
zunyuanjys-MacBook-Air:~ zunyuan.jy$ keytool -list -storetype jceks
輸入金鑰庫口令:
金鑰庫型別: JCEKS
金鑰庫提供方: SunJCE
您的金鑰庫包含 2 個條目
changedi, 2016-7-7, PrivateKeyEntry,
證照指紋 (SHA1): 76:C8:CE:EA:4C:29:6D:0E:FF:8C:02:BE:F4:F4:55:97:63:1F:C8:26
mykeyentry, 2016-7-7, SecretKeyEntry,
其實上面的例子,在建立第一個金鑰項時就可以指定storetype是JCEKS,我這裡只是展示一下如何切換金鑰庫型別。另外在RSA的私鑰金鑰項在未指定證照的情況下也會生成一個自簽名證照。
回到剛才的程式碼裡,我們看看setKeyEntry的細節:
public final void setKeyEntry(String alias, Key key, char[] password,
Certificate[] chain)
throws KeyStoreException
{
if (!initialized) {
throw new KeyStoreException("Uninitialized keystore");
}
if ((key instanceof PrivateKey) &&
(chain == null || chain.length == 0)) {
throw new IllegalArgumentException("Private key must be "
+ "accompanied by certificate "
+ "chain");
}
keyStoreSpi.engineSetKeyEntry(alias, key, password, chain);
}
可以看到,如果是非對稱金鑰的生產,需要提供一個證照鏈,否則就拋異常。考慮到這樣的情況,我們一般不是做專業的企業級安全。還是keytool搞定好了。
相關文章
- 本地金鑰的安全
- 從Java金鑰庫讀取輸出私鑰Java
- Java 混淆那些事(六):Android 混淆的那些瑣事JavaAndroid
- 網路安全那些事兒
- C#和JAVA的RSA金鑰、公鑰轉換C#Java
- Java反射機制那些事Java反射
- JAVA架構師那些事?Java架構
- Java泛型的那些事Java泛型
- 聊聊java就業那些事Java就業
- office 2010 金鑰 office 2010永久的金鑰
- vmware金鑰最新版 vmware金鑰大全
- 金鑰,私鑰,公鑰的區分
- 雲原生java的那些事兒Java
- JAVA執行緒的那些事?Java執行緒
- server2003安裝金鑰 server金鑰序列號Server
- office產品金鑰大全 office產品金鑰分享
- linux中安全和許可權那些事Linux
- vs2015金鑰專業版企業版金鑰大全 visual studio產品金鑰2015
- 談談 Java 中的那些“瑣”事Java
- ArrayList初始化 – Java那些事兒Java
- ArrayList初始化 - Java那些事兒Java
- Android安全開發之淺談金鑰硬編碼Android
- bandizip註冊產品金鑰 bandizip金鑰使用步驟
- bitlocker如何恢復金鑰 bitlocker恢復金鑰的方法
- 在ASP.Net Core和Java中配置金鑰ASP.NETJava
- 讓人疑惑的Java程式碼 – Java那些事兒Java
- Java基礎之執行緒那些事Java執行緒
- Java 資料庫連線的那些事Java資料庫
- 談談java入門的那些事兒Java
- Java 混淆那些事(一):重新認識 ProGuardJava
- Java 混淆那些事(二):認識 ProGuard GUIJavaGUI
- java高併發之ConcurrentSkipListMap的那些事Java
- 雲端計算安全需要將加密金鑰進行控制加密
- oracle 隱式金鑰Oracle
- gitlab配置ssh金鑰Gitlab
- 生成RSA金鑰對
- 【ubuntu】金鑰儲存在過時的 trusted.gpg 金鑰環中UbuntuRust
- vs2012產品金鑰最新 vs2012金鑰使用教程
- git生成ssh金鑰詳細步驟 git如何生成ssh金鑰Git