數字證書簡介及Java編碼實現


1.數字證書簡介

數字證書具備常規加密解密必要的信息,包含簽名算法,可用於網絡數據加密解密交互,標識網絡用戶(計算機)身份。數字證書為發布公鑰提供了一種簡便的途徑,其數字證書則成為加密算法以及公鑰的載體依靠數字證書,我們可以構建一個簡單的加密網絡應用平台。

數字證書類似於個人身份證,由數字證書頒發認證機構(Certificate Authority, CA)簽發。只有經過CA簽發的證書在網絡中才具備可認證性。CA頒發給自己的證書叫根證書。

VeriSign, GeoTrust和Thawte是國際權威數字證書頒發認證機構的三巨頭。其中應用最廣泛的是VeriSign簽發的電子商務用數字證書。

最為常用的非對稱加密算法是RSA,與之配套的簽名算法是SHA1withRSA,最常用的消息摘要算法是SHA1.

 

除了RSA,還可以使用DSA算法。只是使用DSA算法無法完成加密解密實現,即這樣的證書不包括加密解密功能。

數字證書有多種文件編碼格式,主要包含CER編碼,DER編碼等。

CER(Canonical Encoding Rules, 規范編碼格式),DER(Distinguished Encoding Rules 卓越編碼格式),兩者的區別是前者是變長模式,后者是定長模式。

所有證書都符合公鑰基礎設施(PKI, Public Key Infrastructure)制定的ITU-T X509國際標准(X.509標准)。

2.模型分析

在實際應用中,很多數字證書都屬於自簽名證書,即證書申請者為自己的證書簽名。這類證書通常應用於軟件廠商內部發放的產品中,或約定使用該證書的數據交互雙方。數字證書完全充當加密算法的載體,為必要數據做加密解密和簽名驗簽等操作。在我司的開發過程中,數字證書更多是用來做加密和解密。

1)證書簽發

2)加密交互,圖略。

當客戶端獲取到服務器下發的數字證書后,就可以進行加密交互了。具體做法是:

客戶端使用公鑰,加密后發送給服務端,服務端用私鑰進行解密驗證。

服務端使用私鑰進行加密和數字簽名。

3. KeyTool 管理證書

KeyTool與本地密鑰庫相關聯,將私鑰存於密鑰庫,公鑰則以數字證書輸出。KeyTool位於JDK目錄下的bin目錄中,需要通過命令行進行相應的操作。

1)構建自簽名證書

申請數字證書之前,需要在密鑰庫中以別名的方式生成本地數字證書,建立相應的加密算法,密鑰,有效期等信息。

keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 3600 -alias myCertificate -keystore myKeystore.keystore

各參數含義如下:

-genkeypair  表示生成密鑰對

-keyalg    指定密鑰算法,這里是RSA

-keysize    指定密鑰長度,默認1024,這里指定2048

-sigal     指定簽名算法,這里是SHA1withRSA

-validity    指定有效期,單位為天

-alias     指定別名

-keystore    指定密鑰庫存儲位置

這里我輸入參數Changeme123作為密鑰庫的密碼,也可通過參數-storepass指定密碼。可以用-dname "CN=xxx...."這樣的形式,避免更多交互。

注意:一個keystore應該是可以存儲多套<私鑰-數字證書>的信息,通過別名來區分。通過實踐,調用上述命令兩次(別名不同),生成同一個keystore,用不同別名進行加密解密和簽名驗簽,沒有任何問題。

更多命令可參考:http://blog.chinaunix.net/uid-17102734-id-2830223.html 

經過上述操作后,密鑰庫中已經創建了數字證書。雖然這時的數字證書並沒有經過CA認證,但並不影響我們使用。我們仍可將證書導出,發送給合作伙伴進行加密交互。

keytool -exportcert -alias myCertificate -keystore myKeystore.keystore -file myCer.cer -rfc

各參數含義如下:

-exportcert  表示證書導出操作

-alias     指定別名

-keystore   指定密鑰庫文件

-file      指定導出證書的文件路徑

-rfc      指定以Base64編碼格式輸出

打印證書

keytool -printcert -file myCer.cer

2)構建CA簽發證書

如果要獲取CA機構誰的數字證書,需要將數字證書簽發申請(CSR)導出,經由CA機構認證並頒發,將認證后的證書導入本地密鑰庫和信息庫。

keytool -certreq -alias myCertificate -keystore myKeystore.keystore -file myCsr.csr -v

各參數含義如下:

-certreq    表示數字證書申請操作

-alias      指定別名

-keystore    指定密鑰庫文件路徑

-file      指定導出申請的路徑

-v       詳細信息

獲得簽發的數字證書后,需要將其導入信任庫。

keytool -importcert -trustcacerts -alias myCertificate -file myCer.cer -keystore myKeystore.keystore

參數不作詳細講解,如果是原來的證書文件,那么會報錯:

查看證書

keytool -list -alias myCertificate -keystore myKeystore.keystore

 

經過上述的所有操作后,可以得到下面幾個文件

4. 證書使用

終於到了激動人心的時刻,可以用代碼通過keystore進行加解密操作了!

Java 6提供了完善的數字證書管理實現,我們幾乎無需關注,僅通過操作密鑰庫和數字證書就可完成相應的加密解密和簽名驗簽過程。

密鑰庫管理私鑰,數字證書管理公鑰,公鑰和私鑰分屬消息傳遞雙方,進行加密消息傳遞。

考慮一個場景。

A機器某模塊需要將數據導出到一個文件中,將文件發送到B機器,由B將數據導入。

在這個場景中,A就相當於服務端,需要將證書給B,同時用私鑰加密數據,生成簽名,導出到文件中。

B相當於客戶端,用收到的數字證書進行解密和驗簽。

  1 package jdbc.pro.lin;
  2 
  3 import java.io.FileInputStream;
  4 import java.io.FileNotFoundException;
  5 import java.io.IOException;
  6 import java.io.InputStream;
  7 import java.security.InvalidKeyException;
  8 import java.security.KeyStore;
  9 import java.security.KeyStoreException;
 10 import java.security.NoSuchAlgorithmException;
 11 import java.security.PrivateKey;
 12 import java.security.PublicKey;
 13 import java.security.Signature;
 14 import java.security.SignatureException;
 15 import java.security.UnrecoverableKeyException;
 16 import java.security.cert.Certificate;
 17 import java.security.cert.CertificateException;
 18 import java.security.cert.CertificateFactory;
 19 import java.security.cert.X509Certificate;
 20 
 21 import javax.crypto.BadPaddingException;
 22 import javax.crypto.Cipher;
 23 import javax.crypto.IllegalBlockSizeException;
 24 import javax.crypto.NoSuchPaddingException;
 25 
 26 public class MyCertifacate {
 27     private static final String STORE_PASS = "Changeme123";
 28     private static final String ALIAS = "myCertificate";
 29     private static final String KEYSTORE_PATH = "D:\\JavaDemo\\Certifacate\\myKeystore.keystore";
 30     private static final String CERT_PATH = "D:\\JavaDemo\\Certifacate\\myCer.cer";
 31     private static final String PLAIN_TEXT = "MANUTD is the most greatest club in the world.";
 32     /** JDK6只支持X.509標准的證書 */
 33     private static final String CERT_TYPE = "X.509";
 34 
 35     public static void main(String[] args) throws IOException {
 36         /**
 37          * 假設現在有這樣一個場景 。A機器上的數據,需要加密導出,然后將導出文件放到B機器上導入。 在這個場景中,A相當於服務器,B相當於客戶端
 38          */
 39 
 40         /** A */
 41         KeyStore keyStore = getKeyStore(STORE_PASS, KEYSTORE_PATH);
 42         PrivateKey privateKey = getPrivateKey(keyStore, ALIAS, STORE_PASS);
 43         X509Certificate certificate = getCertificateByKeystore(keyStore, ALIAS);
 44 
 45         /** 加密和簽名 */
 46         byte[] encodedText = encode(PLAIN_TEXT.getBytes(), privateKey);
 47         byte[] signature = sign(certificate, privateKey, PLAIN_TEXT.getBytes());
 48 
 49         /** 現在B收到了A的密文和簽名,以及A的可信任證書 */
 50         X509Certificate receivedCertificate = getCertificateByCertPath(
 51                 CERT_PATH, CERT_TYPE);
 52         PublicKey publicKey = getPublicKey(receivedCertificate);
 53         byte[] decodedText = decode(encodedText, publicKey);
 54         System.out.println("Decoded Text : " + new String(decodedText));
 55         System.out.println("Signature is : "
 56                 + verify(receivedCertificate, decodedText, signature));
 57     }
 58 
 59     /**
 60      * 加載密鑰庫,與Properties文件的加載類似,都是使用load方法
 61      * 
 62      * @throws IOException
 63      */
 64     public static KeyStore getKeyStore(String storepass, String keystorePath)
 65             throws IOException {
 66         InputStream inputStream = null;
 67         try {
 68             KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
 69             inputStream = new FileInputStream(keystorePath);
 70             keyStore.load(inputStream, storepass.toCharArray());
 71             return keyStore;
 72         } catch (KeyStoreException | NoSuchAlgorithmException
 73                 | CertificateException | IOException e) {
 74             // TODO Auto-generated catch block
 75             e.printStackTrace();
 76         } finally {
 77             if (null != inputStream) {
 78                 inputStream.close();
 79             }
 80         }
 81         return null;
 82     }
 83 
 84     /**
 85      * 獲取私鑰
 86      * 
 87      * @param keyStore
 88      * @param alias
 89      * @param password
 90      * @return
 91      */
 92     public static PrivateKey getPrivateKey(KeyStore keyStore, String alias,
 93             String password) {
 94         try {
 95             return (PrivateKey) keyStore.getKey(alias, password.toCharArray());
 96         } catch (UnrecoverableKeyException | KeyStoreException
 97                 | NoSuchAlgorithmException e) {
 98             // TODO Auto-generated catch block
 99             e.printStackTrace();
100         }
101         return null;
102     }
103 
104     /**
105      * 獲取公鑰
106      * 
107      * @param certificate
108      * @return
109      */
110     public static PublicKey getPublicKey(Certificate certificate) {
111         return certificate.getPublicKey();
112     }
113 
114     /**
115      * 通過密鑰庫獲取數字證書,不需要密碼,因為獲取到Keystore實例
116      * 
117      * @param keyStore
118      * @param alias
119      * @return
120      */
121     public static X509Certificate getCertificateByKeystore(KeyStore keyStore,
122             String alias) {
123         try {
124             return (X509Certificate) keyStore.getCertificate(alias);
125         } catch (KeyStoreException e) {
126             // TODO Auto-generated catch block
127             e.printStackTrace();
128         }
129         return null;
130     }
131 
132     /**
133      * 通過證書路徑生成證書,與加載密鑰庫差不多,都要用到流。
134      * 
135      * @param path
136      * @param certType
137      * @return
138      * @throws IOException
139      */
140     public static X509Certificate getCertificateByCertPath(String path,
141             String certType) throws IOException {
142         InputStream inputStream = null;
143         try {
144             // 實例化證書工廠
145             CertificateFactory factory = CertificateFactory
146                     .getInstance(certType);
147             // 取得證書文件流
148             inputStream = new FileInputStream(path);
149             // 生成證書
150             Certificate certificate = factory.generateCertificate(inputStream);
151 
152             return (X509Certificate) certificate;
153         } catch (CertificateException | IOException e) {
154             // TODO Auto-generated catch block
155             e.printStackTrace();
156         } finally {
157             if (null != inputStream) {
158                 inputStream.close();
159             }
160         }
161         return null;
162 
163     }
164 
165     /**
166      * 從證書中獲取加密算法,進行簽名
167      * 
168      * @param certificate
169      * @param privateKey
170      * @param plainText
171      * @return
172      */
173     public static byte[] sign(X509Certificate certificate,
174             PrivateKey privateKey, byte[] plainText) {
175         /** 如果要從密鑰庫獲取簽名算法的名稱,只能將其強制轉換成X509標准,JDK 6只支持X.509類型的證書 */
176         try {
177             Signature signature = Signature.getInstance(certificate
178                     .getSigAlgName());
179             signature.initSign(privateKey);
180             signature.update(plainText);
181             return signature.sign();
182         } catch (NoSuchAlgorithmException | InvalidKeyException
183                 | SignatureException e) {
184             // TODO Auto-generated catch block
185             e.printStackTrace();
186         }
187 
188         return null;
189     }
190 
191     /**
192      * 驗簽,公鑰包含在證書里面
193      * 
194      * @param certificate
195      * @param decodedText
196      * @param receivedignature
197      * @return
198      */
199     public static boolean verify(X509Certificate certificate,
200             byte[] decodedText, final byte[] receivedignature) {
201         try {
202             Signature signature = Signature.getInstance(certificate
203                     .getSigAlgName());
204             /** 注意這里用到的是證書,實際上用到的也是證書里面的公鑰 */
205             signature.initVerify(certificate);
206             signature.update(decodedText);
207             return signature.verify(receivedignature);
208         } catch (NoSuchAlgorithmException | InvalidKeyException
209                 | SignatureException e) {
210             // TODO Auto-generated catch block
211             e.printStackTrace();
212         }
213         return false;
214     }
215 
216     /**
217      * 加密。注意密鑰是可以獲取到它適用的算法的。
218      * 
219      * @param plainText
220      * @param privateKey
221      * @return
222      */
223     public static byte[] encode(byte[] plainText, PrivateKey privateKey) {
224         try {
225             Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
226             cipher.init(Cipher.ENCRYPT_MODE, privateKey);
227             return cipher.doFinal(plainText);
228         } catch (NoSuchAlgorithmException | NoSuchPaddingException
229                 | InvalidKeyException | IllegalBlockSizeException
230                 | BadPaddingException e) {
231             // TODO Auto-generated catch block
232             e.printStackTrace();
233         }
234 
235         return null;
236 
237     }
238 
239     /**
240      * 解密,注意密鑰是可以獲取它適用的算法的。
241      * 
242      * @param encodedText
243      * @param publicKey
244      * @return
245      */
246     public static byte[] decode(byte[] encodedText, PublicKey publicKey) {
247         try {
248             Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
249             cipher.init(Cipher.DECRYPT_MODE, publicKey);
250             return cipher.doFinal(encodedText);
251         } catch (NoSuchAlgorithmException | NoSuchPaddingException
252                 | InvalidKeyException | IllegalBlockSizeException
253                 | BadPaddingException e) {
254             // TODO Auto-generated catch block
255             e.printStackTrace();
256         }
257 
258         return null;
259     }
260 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM