View Javadoc
1   /**
2    * Copyright By Grandsoft Company Limited.  
3    * 2014年3月24日 上午9:22:16
4    */
5   package gboat2.base.bridge.util.security;
6   
7   import java.io.FileInputStream;
8   import java.io.FileNotFoundException;
9   import java.security.KeyStore;
10  import java.security.KeyStoreException;
11  import java.security.PrivateKey;
12  import java.security.PublicKey;
13  import java.security.cert.Certificate;
14  import java.security.cert.CertificateFactory;
15  import java.security.cert.X509Certificate;
16  import java.util.Date;
17  
18  import org.apache.commons.io.IOUtils;
19  
20  /**
21   * 对加密、解密证书进行操作的工具类。
22   * JDK 中与证书操作相关的命令: 
23   * <pre>
24   * 1. 生成keyStroe文件: 
25   * <code>keytool -genkey -validity 36000 -alias www.glodon.com -keyalg RSA -keystore D:/glodon.keystore</code>
26   * 其中
27   *     <b>-genkey</b> 表示生成密钥 
28   *     <b>-validity</b> 指定证书有效期,这里是 36000 天 
29   *     <b>-alias</b> 指定别名,这里是 www.glodon.com 
30   *     <b>-keyalg</b> 指定算法,这里是 RSA 
31   *     <b>-keystore</b> 指定存储位置,这里是 D:/glodon.keystore 
32   * 控制台输出:
33   *     输入keystore密码:  
34   *     再次输入新密码:  
35   *     您的名字与姓氏是什么?  
36   *       [Unknown]:  www.glodon.com  
37   *     您的组织单位名称是什么?  
38   *       [Unknown]:  glodon  
39   *     您的组织名称是什么?  
40   *       [Unknown]:  glodon  
41   *     您所在的城市或区域名称是什么?  
42   *       [Unknown]:  BeiJing  
43   *     您所在的州或省份名称是什么?  
44   *       [Unknown]:  BeiJing  
45   *     该单位的两字母国家代码是什么  
46   *       [Unknown]:  CN  
47   *     CN=www.glodon.com, OU=glodon, O=glodon, L=BeiJing, ST=BeiJing, C=CN 正确吗?  
48   *       [否]:  Y  
49   *       
50   *     输入 &lt;tomcat&gt; 的主密码  
51   *             (如果和 keystore 密码相同,按回车):  
52   *     再次输入新密码:  
53   * 这时,在 D 盘下会生成一个 glodon.keystore 的文件。 
54   * 
55   * 2. 生成自签名证书 ,光有keyStore文件是不够的,还需要证书文件,证书才是直接提供给外界使用的公钥凭证。 
56   * 导出证书: 
57   * <code>keytool -export -keystore d:\glodon.keystore -alias www.glodon.com -file D:/glodon.cer -rfc</code>
58   * 其中 
59   *     <b>-export</b> 指定为导出操作 
60   *     <b>-keystore</b> 指定 keystore 文件 
61   *     <b>-alias</b> 指定导出 keystore 文件中的别名 
62   *     <b>-file</b> 指向导出路径 
63   *     <b>-rfc</b> 以文本格式输出,也就是以 BASE64 编码输出 
64   * 控制台输出:
65   *     输入keystore密码:  
66   *     保存在文件中的认证 <D:/glodon.cer>
67   *     
68   * 普及一下使用 JarSigner 对代码进行签名的小知识: 
69   * <code>jarsigner -storetype jks -keystore glodon.keystore -verbose glodon-example.jar www.glodon.com</code>
70   * 控制台输出:
71   *     输入密钥库的口令短语:  
72   *      正在更新: META-INF/WWW_GLODON.SF  
73   *      正在更新: META-INF/WWW_GLODON.RSA  
74   *       正在签名: org/glodon/security/Security.class  
75   *       正在签名: org/glodon/example/Main$1.class  
76   *       正在签名: org/glodon/example/Main$2.class  
77   *       正在签名: org/glodon/example/Main.class  
78   *       
79   *     警告:  
80   *     签名者证书将在六个月内过期。  
81   * 
82   * 签名完成后,还可以使用 JarSigner 对签名后的 jar 进行校验: 
83   * <code>jarsigner -verify -verbose -certs glodon-example.jar</code>
84   * 控制台输出:
85   *               402 Sat Jun 20 16:25:14 CST 2009 META-INF/MANIFEST.MF  
86   *               532 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_GLODON.SF  
87   *               889 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_GLODON.RSA  
88   *      sm       590 Wed Dec 10 13:03:42 CST 2014 org/glodon/security/Security.class  
89   *        
90   *            X.509, CN=www.glodon.com, OU=glodon, O=glodon, L=BeiJing, ST=BeiJing, C=CN  
91   *            [证书将在 09-9-18 下午3:27 到期]  
92   *        
93   *      sm       705 Tue Dec 16 18:00:56 CST 2014 org/glodon/example/Main$1.class  
94   *        
95   *            X.509, CN=www.glodon.com, OU=glodon, O=glodon, L=BeiJing, ST=BeiJing, C=CN  
96   *            [证书将在 09-9-18 下午3:27 到期]  
97   *        
98   *      sm       779 Tue Dec 16 18:00:56 CST 2014 org/glodon/example/Main$2.class  
99   *        
100  *            X.509, CN=www.glodon.com, OU=glodon, O=glodon, L=BeiJing, ST=BeiJing, C=CN  
101  *            [证书将在 09-9-18 下午3:27 到期]  
102  *        
103  *      sm     12672 Tue Dec 16 18:00:56 CST 2014 org/glodon/example/Main.class  
104  *        
105  *            X.509, CN=www.glodon.com, OU=glodon, O=glodon, L=BeiJing, ST=BeiJing, C=CN  
106  *            [证书将在 09-9-18 下午3:27 到期]  
107  *        
108  *        
109  *        s = 已验证签名  
110  *        m = 在清单中列出条目  
111  *        k = 在密钥库中至少找到了一个证书  
112  *        i = 在身份作用域内至少找到了一个证书  
113  *        
114  *      jar 已验证。  
115  *        
116  *      警告:  
117  *      此 jar 包含签名者证书将在六个月内过期的条目。  
118  * </pre>
119  * @author <a href="mailto:[email protected]">何明旺</a>
120  * @since 3.0
121  * @date 2014年3月24日
122  * @see RSAUtil
123  * @see RSASignUtil
124  */
125 public class CertificateUtil {
126     public static final String PKCS12 = "PKCS12";
127 
128     /** Java密钥库(Java 密钥库,JKS)KEY_STORE */
129     public static final String KEY_STORE = "JKS";
130 
131     public static final String X509 = "X.509";
132     
133     /**
134      * 获得密钥库
135      * @param keyStorePath 密钥库存储路径
136      * @param password 密钥库密码
137      * @return
138      */
139     public static KeyStore getKeyStore(String keyStorePath, String password) {
140         FileInputStream in = null;
141         try {
142             in = new FileInputStream(keyStorePath);
143             KeyStore keyStore = KeyStore.getInstance(KEY_STORE);
144             keyStore.load(in, password.toCharArray());
145             return keyStore;
146         } catch (FileNotFoundException e) {
147             throw new GboatSecurityException("密钥库文件 [" + keyStorePath + "] 不存在", e);
148         } catch (Exception e) {
149             throw new GboatSecurityException("加载密钥库失败:keyStorePath=" + keyStorePath + ", password=" + password, e);
150         } finally {
151             IOUtils.closeQuietly(in);
152         }
153     }
154 
155     /**
156      * 根据密钥库获得私钥
157      * @param keyStorePath 密钥库存储路径
158      * @param alias 密钥库别名
159      * @param password 密钥库密码
160      * @return
161      */
162     public static PrivateKey getPrivateKey(String keyStorePath, String alias, String password) {
163         KeyStore keyStore = getKeyStore(keyStorePath, password);
164         try {
165             return (PrivateKey) keyStore.getKey(alias, password.toCharArray());
166         } catch (Exception e) {
167             throw new GboatSecurityException("从密钥库中读取私钥信息失败:keyStorePath=" + keyStorePath + ", password=" + password + ", alias=" + alias, e);
168         }
169     }
170 
171     /**
172      * 根据证书获得公钥
173      * 
174      * @param certificatePath 证书存储路径
175      * @return
176      */
177     public static PublicKey getPublicKey(String certificatePath) {
178         Certificate certificate = getCertificate(certificatePath);
179         return certificate.getPublicKey();
180     }
181 
182     /**
183      * 获得证书
184      * 
185      * @param certificatePath 证书存储路径
186      * @return
187      */
188     public static Certificate getCertificate(String certificatePath) {
189         FileInputStream in = null;
190         try {
191             CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);
192             in = new FileInputStream(certificatePath);
193             return certificateFactory.generateCertificate(in);
194         } catch (FileNotFoundException e) {
195             throw new GboatSecurityException("证书文件 [" + certificatePath + "] 不存在", e);
196         } catch (Exception e) {
197             throw new GboatSecurityException("加载证书 [" + certificatePath + "] 失败", e);
198         } finally {
199             IOUtils.closeQuietly(in);
200         }
201     }
202 
203     /**
204      * 根据密钥库获得证书
205      * 
206      * @param keyStorePath 密钥库存储路径
207      * @param alias 密钥库别名
208      * @param password 密钥库密码
209      * @return
210      */
211     public static Certificate getCertificate(String keyStorePath, String alias, String password) {
212         KeyStore keyStore = getKeyStore(keyStorePath, password);
213         try {
214             return keyStore.getCertificate(alias);
215         } catch (KeyStoreException e) {
216             throw new GboatSecurityException("根据密钥库加载证书失败:keyStorePath=" + keyStorePath + ", password=" + password + ", alias=" + alias, e);
217         }
218     }
219 
220     /**
221      * 验证数字证书当前是否有效
222      * 
223      * @param certificatePath 证书存储路径
224      * @return
225      */
226     public static boolean verifyCertificate(String certificatePath) {
227         return verifyCertificate(certificatePath, new Date());
228     }
229 
230     /**
231      * 验证数字证书是在给定的日期是否有效
232      * 
233      * @param date 日期
234      * @param certificatePath 证书存储路径
235      * @return
236      */
237     public static boolean verifyCertificate(String certificatePath, Date date) {
238         Certificate certificate = getCertificate(certificatePath);
239         if(!(certificate instanceof X509Certificate))
240             throw new GboatSecurityException("证书 [" + certificatePath + "] 的类型不是 X.509,暂不支持对其进行有效性校验。");
241         
242         return verifyCertificate((X509Certificate)certificate, date);
243     }
244 
245     /**
246      * 校验证书当前是否有效
247      * 
248      * @param certificate 证书
249      * @return
250      */
251     public static boolean verifyCertificate(X509Certificate certificate) {
252         return verifyCertificate(certificate, new Date());
253     }
254     
255     /**
256      * 验证证书是否过期或无效
257      * 
258      * @param date 日期
259      * @param certificate 证书
260      * @return
261      */
262     public static boolean verifyCertificate(X509Certificate certificate, Date date) {
263         try {
264             certificate.checkValidity(date);
265             return true;
266         } catch (Exception e) {
267             return false;
268         }
269     }
270 
271     /**
272      * 验证密钥库对应的数字证书当前是否有效
273      * 
274      * @param keyStorePath 密钥库存储路径
275      * @param alias 密钥库别名
276      * @param password 密钥库密码
277      * @return
278      */
279     public static boolean verifyCertificate(String keyStorePath, String alias, String password) {
280         return verifyCertificate(new Date(), keyStorePath, alias, password);
281     }
282     
283     /**
284      * 验证密钥库对应的数字证书是在给定的日期是否有效
285      * 
286      * @param keyStorePath 密钥库存储路径
287      * @param alias 密钥库别名
288      * @param password 密钥库密码
289      * @return
290      */
291     public static boolean verifyCertificate(Date date, String keyStorePath, String alias, String password) {
292         Certificate certificate = getCertificate(keyStorePath, alias, password);
293         if(!(certificate instanceof X509Certificate))
294             throw new GboatSecurityException("密钥库对应 [" + keyStorePath + "] 的证书类型不是 X.509,暂不支持对其进行有效性校验。");
295         
296         return verifyCertificate((X509Certificate)certificate, date);
297     }
298 
299     /**
300      * 根据密钥库获取其签名算法
301      * 
302      * @param keyStorePath 密钥库存储路径
303      * @param alias 密钥库别名
304      * @param password 密钥库密码
305      * @return
306      */
307     public static String getSigAlgName(String keyStorePath, String alias, String password) {
308         // 获得证书
309         Certificate certificate = getCertificate(keyStorePath, alias, password);
310         if(!(certificate instanceof X509Certificate))
311             throw new GboatSecurityException("密钥库对应 [" + keyStorePath + "] 的证书类型不是 X.509,无法读取该密钥库对应的签名算法。");
312         
313         return ((X509Certificate) certificate).getSigAlgName();
314     }
315 
316 }