1 数字证书
1.1 概述
数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在Internet上验证通信实体身份的方式,数字证书不是数字身份证,而是身份认证机构盖在数字身份证上的一个章或印(或者说加在数字身份证上的一个签名)。它是由权威机构——CA机构,又称为证书授权(Certificate Authority)中心发行的,人们可以在网上用它来识别对方的身份。
2 证书格式
2.1 证书格式分类
分为2大类:密钥库(含私钥,也可能有公钥)和公钥证书(仅含公钥)
2.1.1 密钥库文件格式【Keystore】
格式 : JKS 扩展名 : .jks/.ks 描述 : 【Java Keystore】密钥库的Java实现版本,provider为SUN 特点 : 密钥库和私钥用不同的密码进行保护 格式 : JCEKS 扩展名 : .jce 描述 : 【JCE Keystore】密钥库的JCE实现版本,provider为SUN JCE 特点 : 相对于JKS安全级别更高,保护Keystore私钥时采用TripleDES 格式 : PKCS12 扩展名 : .p12/.pfx 描述 : 【PKCS #12】个人信息交换语法标准 特点 : 1、包含私钥、公钥及其证书 2、密钥库和私钥用相同密码进行保护 格式 : BKS 扩展名 : .bks 描述 : Bouncycastle Keystore】密钥库的BC实现版本,provider为BC 特点 : 基于JCE实现 格式 : UBER 扩展名 : .ubr 描述 : 【Bouncycastle UBER Keystore】密钥库的BC更安全实现版本,provider为BC
2.2.2 证书文件格式【Certificate】
格式 : DER 扩展名 : .cer/.crt/.rsa
描述 : 【ASN .1 DER】用于存放证书 特点 : 不含私钥、二进制
格式 : PKCS7 扩展名 : .p7b/.p7r 描述 : 【PKCS #7】加密信息语法标准
特点 : 1、p7b以树状展示证书链,不含私钥 2、p7r为CA对证书请求签名的回复,只能用于导入
格式 : CMS 扩展名 : .p7c/.p7m/.p7s 描述 : 【Cryptographic Message Syntax】 特点 : 1、p7c只保存证书 2、p7m:signature with enveloped data 3、p7s:时间戳签名文件 格式 : PEM扩展名 : .pem 描述 : 【Printable Encoded Message】 特点 : 1、该编码格式在RFC1421中定义,其实PEM是【Privacy-Enhanced Mail】的简写,但他也同样广泛运用于密钥管理 2、ASCII文件 3、一般基于base 64编码
格式 : PKCS10 扩展名 : .p10/.csr 描述 : 【PKCS #10】公钥加密标准【Certificate Signing Request】特点 : 1、证书签名请求文件 2、ASCII文件 3、CA签名后以p7r文件回复
格式 : SPC 扩展名 : .pvk/.spc 描述 : 【Software Publishing Certificate】 特点 : 微软公司特有的双证书文件格式,经常用于代码签名,其中 1、pvk用于保存私钥 2、spc用于保存公钥
2.3 常用证书文件格式
CA中心普遍采用的规范是X.509[13]系列和PKCS系列,其中主要应用到了以下规范:
2.3.1 X.509(1993)
X.509是由国际电信联盟(ITU-T)制定的数字证书标准。在X.500确保用户名称惟一性的基础上,X.509为X.500用户名称提供了通信实体的鉴别机制,并规定了实体鉴别过程中广泛适用的证书语法和数据接口。
X.509的最初版本公布于1988年。X.509证书由用户公共密钥和用户标识符组成。此外还包括版本号、证书序列号、CA标识符、签名算法标识、签发者名称、证书有效期等信息。这一标准的最新版本是X.509 v3,它定义了包含扩展信息的数字证书。该版数字证书提供了一个扩展信息字段,用来提供更多的灵活性及特殊应用环境下所需的信息传送。
一个标准的X.509数字证书包含以下一些内容:
证书的版本信息; 证书的序列号,每个证书都有一个唯一的证书序列号; 证书所使用的签名算法; 证书的发行机构名称,命名规则一般采用X.500格式; 证书的有效期,通用的证书一般采用UTC时间格式,它的计时范围为1950-2049; 证书所有人的名称,命名规则一般采用X.500格式; 证书所有人的公开密钥; 证书发行者对证书的签名。
2.3.2 PKCS系列标准
PKCS是由美国RSA数据安全公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。到1999年底,PKCS已经公布了以下标准:
PKCS#1:定义RSA公开密钥算法加密和签名机制,主要用于组织PKCS#7中所描述的数字签名和数字信封。
PKCS#3:定义Diffie-Hellman密钥交换协议。
PKCS#5:描述一种利用从口令派生出来的安全密钥加密字符串的方法。使用MD2或MD5 从口令中派生密钥,并采用DES-CBC模式加密。主要用于加密从一个计算机传送到另一个计算机的私人密钥,不能用于加密消息。
PKCS#6:描述了公钥证书的标准语法,主要描述X.509证书的扩展格式。
PKCS#7:定义一种通用的消息语法,包括数字签名和加密等用于增强的加密机制,PKCS#7与PEM兼容,所以不需其他密码操作,就可以将加密的消息转换成PEM消息。
PKCS#8:描述私有密钥信息格式,该信息包括公开密钥算法的私有密钥以及可选的属性集等。
PKCS#9:定义一些用于PKCS#6证书扩展、PKCS#7数字签名和PKCS#8私钥加密信息的属性类型。
PKCS#10:描述证书请求语法。
PKCS#11:称为Cyptoki,定义了一套独立于技术的程序设计接口,用于智能卡和PCMCIA卡之类的加密设备。
PKCS#12:描述个人信息交换语法标准。描述了将用户公钥、私钥、证书和其他相关信息打包的语法。
PKCS#13:椭圆曲线密码体制标准。
PKCS#14:伪随机数生成标准。
PKCS#15:密码令牌信息格式标准。
2.3.3 数字证书文件格式(cer和pfx)的区别
作为文件形式存在的证书一般有这几种格式:
1.带有私钥的证书由Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形式,以pfx作为证书文件后缀名。
2.二进制编码的证书 证书中没有私钥,DER 编码二进制格式的证书文件,以cer作为证书文件后缀名。
3.Base64编码的证书证书中没有私钥,BASE64 编码格式的证书文件,也是以cer作为证书文件后缀名。
由定义可以看出,只有pfx格式的数字证书是包含有私钥的,cer格式的数字证书里面只有公钥没有私钥。
3 java代码实现(读取证书密钥)
1 package cn.mars.app.txn.xmmsi;
2
3 import java.io.BufferedReader;
4 import java.io.ByteArrayInputStream;
5 import java.io.ByteArrayOutputStream;
6 import java.io.FileInputStream;
7 import java.io.FileNotFoundException;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.InputStreamReader;
11 import java.math.BigInteger;
12 import java.security.InvalidAlgorithmParameterException;
13 import java.security.InvalidKeyException;
14 import java.security.KeyFactory;
15 import java.security.KeyStore;
16 import java.security.KeyStoreException;
17 import java.security.NoSuchAlgorithmException;
18 import java.security.PrivateKey;
19 import java.security.PublicKey;
20 import java.security.Signature;
21 import java.security.SignatureException;
22 import java.security.UnrecoverableKeyException;
23 import java.security.cert.Certificate;
24 import java.security.cert.CertificateException;
25 import java.security.cert.CertificateFactory;
26 import java.security.cert.X509Certificate;
27 import java.security.spec.InvalidKeySpecException;
28 import java.security.spec.KeySpec;
29 import java.security.spec.PKCS8EncodedKeySpec;
30 import java.security.spec.RSAPrivateKeySpec;
31 import java.security.spec.RSAPublicKeySpec;
32 import java.security.spec.X509EncodedKeySpec;
33 import java.util.Enumeration;
34
35 import javax.crypto.BadPaddingException;
36 import javax.crypto.Cipher;
37 import javax.crypto.IllegalBlockSizeException;
38 import javax.crypto.NoSuchPaddingException;
39 import javax.crypto.SecretKey;
40 import javax.crypto.spec.IvParameterSpec;
41 import javax.crypto.spec.SecretKeySpec;
42
43 import org.apache.commons.codec.binary.Base64;
44 import org.apache.commons.lang.StringUtils;
45 import org.bouncycastle.asn1.ASN1InputStream;
46 import org.bouncycastle.asn1.ASN1Sequence;
47 import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
48 import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
49 /*import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;*/
51 import cn.mars.common.component.logger.LOG;
52 import cn.mars.common.component.logger.LogFactory;
53 import cn.mars.app.txn.xmmsi.KeyUtil;
54
55 /**
56 * Title : CryptoUtil
57 * Description : 加解密工具类
58 * Create on : 2015-05-04
59 *
60 * @author linshangqing@cmbc.com.cn
61 */
62 public abstract class CryptoUtil {
63 /**
64 * 日志对象
65 */
66 public static LOG logger = LogFactory.getLogger(CryptoUtil.class);
67 /**
68 * 数字签名函数入口
69 *
70 * @param plainBytes
71 * 待签名明文字节数组
72 * @param privateKey
73 * 签名使用私钥
74 * @param signAlgorithm
75 * 签名算法
76 * @return 签名后的字节数组
77 * @throws Exception
78 */
79 public static byte[] digitalSign(byte[] plainBytes, PrivateKey privateKey, String signAlgorithm) throws Exception {
80 try {
81 Signature signature = Signature.getInstance(signAlgorithm);
82 signature.initSign(privateKey);
83 signature.update(plainBytes);
84 byte[] signBytes = signature.sign();
85
86 return signBytes;
87 } catch (NoSuchAlgorithmException e) {
88 throw new Exception(String.format("数字签名时没有[%s]此类算法", signAlgorithm));
89 //throw new Exception(String.format("数字签名时没有[%s]此类算法", signAlgorithm));
90 } catch (InvalidKeyException e) {
91 throw new Exception("数字签名时私钥无效");
92 } catch (SignatureException e) {
93 throw new Exception("数字签名时出现异常");
94 }
95 }
96
97 /**
98 * 验证数字签名函数入口
99 *
100 * @param plainBytes
101 * 待验签明文字节数组
102 * @param signBytes
103 * 待验签签名后字节数组
104 * @param publicKey
105 * 验签使用公钥
106 * @param signAlgorithm
107 * 签名算法
108 * @return 验签是否通过
109 * @throws Exception
110 */
111 public static boolean verifyDigitalSign(byte[] plainBytes, byte[] signBytes, PublicKey publicKey, String signAlgorithm) throws Exception {
112 boolean isValid = false;
113 try {
114 Signature signature = Signature.getInstance(signAlgorithm);
115 signature.initVerify(publicKey);
116 signature.update(plainBytes);
117 isValid = signature.verify(signBytes);
118 return isValid;
119 } catch (NoSuchAlgorithmException e) {
120 throw new Exception(String.format("验证数字签名时没有[%s]此类算法", signAlgorithm));
121 } catch (InvalidKeyException e) {
122 throw new Exception("验证数字签名时公钥无效");
123 } catch (SignatureException e) {
124 throw new Exception("验证数字签名时出现异常");
125 }
126 }
127
128 /**
129 * 验证数字签名函数入口
130 *
131 * @param plainBytes
132 * 待验签明文字节数组
133 * @param signBytes
134 * 待验签签名后字节数组
135 * @param publicKey
136 * 验签使用公钥
137 * @param signAlgorithm
138 * 签名算法
139 * @return 验签是否通过
140 * @throws Exception
141 */
142 public static boolean verifyDigitalSign(byte[] plainBytes, byte[] signBytes, X509Certificate cert, String signAlgorithm) throws Exception {
143 boolean isValid = false;
144 try {
145 Signature signature = Signature.getInstance(signAlgorithm);
146 signature.initVerify(cert);
147 signature.update(plainBytes);
148 isValid = signature.verify(signBytes);
149 return isValid;
150 } catch (NoSuchAlgorithmException e) {
151 throw new Exception(String.format("验证数字签名时没有[%s]此类算法", signAlgorithm));
152 } catch (InvalidKeyException e) {
153 throw new Exception("验证数字签名时公钥无效");
154 } catch (SignatureException e) {
155 throw new Exception("验证数字签名时出现异常");
156 }
157 }
158 /**
159 * 获取RSA公钥对象
160 *
161 * @param filePath
162 * RSA公钥路径
163 * @param fileSuffix
164 * RSA公钥名称,决定编码类型
165 * @param keyAlgorithm
166 * 密钥算法
167 * @return RSA公钥对象
168 * @throws Exception
169 */
170 public static PublicKey getRSAPublicKeyByFileSuffix(String filePath, String fileSuffix, String keyAlgorithm) throws Exception {
171 InputStream in = null;
172 String keyType = "";
173 if ("crt".equalsIgnoreCase(fileSuffix) || "txt".equalsIgnoreCase(fileSuffix) ||"cer".equalsIgnoreCase(fileSuffix)) {
174 keyType = "X.509";
175 } else if ("pem".equalsIgnoreCase(fileSuffix)) {
176 keyType = "PKCS12";
177 } else if(("yljf").equalsIgnoreCase(fileSuffix)){
178 keyType = "yljf";
179 } else{
180 keyType = "PKCS12";
181 }
182
183 try {
184 in = new FileInputStream(filePath);
185 PublicKey pubKey = null;
186 if ("X.509".equals(keyType)) {
187 CertificateFactory factory = CertificateFactory.getInstance(keyType);
188 Certificate cert = factory.generateCertificate(in);
189 pubKey = cert.getPublicKey();
190 } else if ("PKCS12".equals(keyType)) {
191 BufferedReader br = new BufferedReader(new InputStreamReader(in));
192 StringBuilder sb = new StringBuilder();
193 String readLine = null;
194 while ((readLine = br.readLine()) != null) {
195 if (readLine.charAt(0) == '-') {
196 continue;
197 } else {
198 sb.append(readLine);
199 sb.append('\r');
200 }
201 }
202 X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(Base64.decodeBase64(sb.toString()));
203 KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
204 pubKey = keyFactory.generatePublic(pubX509);
205 }else if("yljf".equals(keyType)){
206 BufferedReader br = new BufferedReader(new InputStreamReader(in, "utf-8"));
207 String s = br.readLine();
208 ASN1InputStream ain = new ASN1InputStream(hexString2ByteArr(s));
209 RSAPublicKeyStructure pStruct = RSAPublicKeyStructure.getInstance(ain.readObject());
210 RSAPublicKeySpec spec = new RSAPublicKeySpec(pStruct.getModulus(), pStruct.getPublicExponent());
211 KeyFactory kf = KeyFactory.getInstance("RSA");
212 if (in != null)
213 in.close();
214 return kf.generatePublic(spec);
215 }
216
217 return pubKey;
218 } catch (FileNotFoundException e) {
219 throw new Exception("公钥路径文件不存在");
220 } catch (CertificateException e) {
221 throw new Exception("生成证书文件错误");
222 } catch (IOException e) {
223 throw new Exception("读取公钥异常");
224 } catch (NoSuchAlgorithmException e) {
225 throw new Exception(String.format("生成密钥工厂时没有[%s]此类算法", keyAlgorithm));
226 } catch (InvalidKeySpecException e) {
227 throw new Exception("生成公钥对象异常");
228 } finally {
229 try {
230 if (in != null) {
231 in.close();
232 }
233 } catch (IOException e) {
234 }
235 }
236 }
237
238 /**
239 *
240 *
description : 生成平台公钥证书对象
241 * @param b
242 * @return
243 * @version 1.0
244 * @date 2014-7-25上午11:56:05
245 */
246 private static X509Certificate getCert(String filePath) throws Exception {
247 try {
248 byte[] b = null;
249 InputStream in = null;
250 try{
251 in = new FileInputStream(filePath);
252 b = new byte[20480];
253 in.read(b);
254 }finally{
255 if(null!=in)in.close();
256 }
257 ByteArrayInputStream bais = new ByteArrayInputStream(b);
258 CertificateFactory cf = CertificateFactory.getInstance("X.509");
259 X509Certificate x509Certificate = (X509Certificate)cf.generateCertificate(bais);
260 return x509Certificate;
261 } catch (Exception e) {
262 logger.error("",e);
263 throw new Exception("生成公钥证书对象异常");
264 }
265 }
266
267 /**
268 * 获取RSA私钥对象
269 *
270 * @param filePath
271 * RSA私钥路径
272 * @param fileSuffix
273 * RSA私钥名称,决定编码类型
274 * @param password
275 * RSA私钥保护密钥
276 * @param keyAlgorithm
277 * 密钥算法
278 * @return RSA私钥对象
279 * @throws Exception
280 */
281 @SuppressWarnings("deprecation")
282 public static PrivateKey getRSAPrivateKeyByFileSuffix(String filePath, String fileSuffix, String password, String keyAlgorithm)
283 throws Exception {
284 String keyType = "";
285 if ("keystore".equalsIgnoreCase(fileSuffix)) {
286 keyType = "JKS";
287 } else if ("pfx".equalsIgnoreCase(fileSuffix) || "p12".equalsIgnoreCase(fileSuffix)) {
288 keyType = "PKCS12";
289 } else if ("jck".equalsIgnoreCase(fileSuffix)) {
290 keyType = "JCEKS";
291 } else if ("pem".equalsIgnoreCase(fileSuffix) || "pkcs8".equalsIgnoreCase(fileSuffix)) {
292 keyType = "PKCS8";
293 } else if ("pkcs1".equalsIgnoreCase(fileSuffix)) {
294 keyType = "PKCS1";
295 } else if ("yljf".equalsIgnoreCase(fileSuffix)) {
296 keyType = "yljf";
297 } else if ("ldys".equalsIgnoreCase(fileSuffix)) {
298 keyType = "ldys";
299 } else{
300 keyType = "JKS";
301 }
302
303 InputStream in = null;
304 try {
305 in = new FileInputStream(filePath);
306 PrivateKey priKey = null;
307 if ("JKS".equals(keyType) || "PKCS12".equals(keyType) || "JCEKS".equals(keyType)) {
308 KeyStore ks = KeyStore.getInstance(keyType);
309 if (password != null) {
310 char[] cPasswd = password.toCharArray();
311 ks.load(in, cPasswd);
312 Enumeration
313 String keyAlias = null;
314 while (aliasenum.hasMoreElements()) {
315 keyAlias = (String) aliasenum.nextElement();
316 priKey = (PrivateKey) ks.getKey(keyAlias, cPasswd);
317 if (priKey != null)
318 break;
319 }
320 }
321 }else if("yljf".equals(keyType)){
322 BufferedReader br = new BufferedReader(new InputStreamReader(in));
323 String s = br.readLine();
324 PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(hexStrToBytes(s));
325 KeyFactory keyf=KeyFactory.getInstance("RSA");
326 PrivateKey myprikey=keyf.generatePrivate(priPKCS8);
327 return myprikey;
328 }else if("ldys".equals(keyType)){
329 byte[] b = new byte[20480];
330 in.read(b);
331 PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(b);
332 KeyFactory keyf=KeyFactory.getInstance("RSA");
333 PrivateKey myprikey=keyf.generatePrivate(priPKCS8);
334 return myprikey;
335 }else {
336 BufferedReader br = new BufferedReader(new InputStreamReader(in));
337 StringBuilder sb = new StringBuilder();
338 String readLine = null;
339 while ((readLine = br.readLine()) != null) {
340 if (readLine.charAt(0) == '-') {
341 continue;
342 } else {
343 sb.append(readLine);
344 sb.append('\r');
345 }
346 }
347 if ("PKCS8".equals(keyType)) {
348 PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(sb.toString()));
349 KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
350 priKey = keyFactory.generatePrivate(priPKCS8);
351 } else if ("PKCS1".equals(keyType)) {
352 // RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(sb.toString().getBytes()));
353 RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(Base64.decodeBase64(sb.toString())));
354 KeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());
355 KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
356 priKey = keyFactory.generatePrivate(rsaPrivKeySpec);
357 }
358 }
359
360 return priKey;
361 } catch (FileNotFoundException e) {
362 throw new Exception("私钥路径文件不存在");
363 } catch (KeyStoreException e) {
364 throw new Exception("获取KeyStore对象异常");
365 } catch (IOException e) {
366 throw new Exception("读取私钥异常");
367 } catch (NoSuchAlgorithmException e) {
368 throw new Exception("生成私钥对象异常");
369 } catch (CertificateException e) {
370 throw new Exception("加载私钥密码异常");
371 } catch (UnrecoverableKeyException e) {
372 throw new Exception("生成私钥对象异常");
373 } catch (InvalidKeySpecException e) {
374 throw new Exception("生成私钥对象异常");
375 } finally {
376 try {
377 if (in != null) {
378 in.close();
379 }
380 } catch (IOException e) {
381 }
382 }
383 }
384
385 /**
386 * RSA加密
387 *
388 * @param plainBytes
389 * 明文字节数组
390 * @param publicKey
391 * 公钥
392 * @param keyLength
393 * 密钥bit长度
394 * @param reserveSize
395 * padding填充字节数,预留11字节
396 * @param cipherAlgorithm
397 * 加解密算法,一般为RSA/ECB/PKCS1Padding
398 * @return 加密后字节数组,不经base64编码
399 * @throws Exception
400 */
401 public static byte[] RSAEncrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm)
402 throws Exception {
403 int keyByteSize = keyLength / 8; // 密钥字节数
404 int encryptBlockSize = keyByteSize - reserveSize; // 加密块大小=密钥字节数-padding填充字节数
405 int nBlock = plainBytes.length / encryptBlockSize;// 计算分段加密的block数,向上取整
406 if ((plainBytes.length % encryptBlockSize) != 0) { // 余数非0,block数再加1
407 nBlock += 1;
408 }
409
410 try {
411 Cipher cipher = Cipher.getInstance(cipherAlgorithm);
412 cipher.init(Cipher.ENCRYPT_MODE, publicKey);
413
414 // 输出buffer,大小为nBlock个keyByteSize
415 ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
416 // 分段加密
417 for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
418 int inputLen = plainBytes.length - offset;
419 if (inputLen > encryptBlockSize) {
420 inputLen = encryptBlockSize;
421 }
422
423 // 得到分段加密结果
424 byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
425 // 追加结果到输出buffer中
426 outbuf.write(encryptedBlock);
427 }
428
429 outbuf.flush();
430 outbuf.close();
431 return outbuf.toByteArray();
432 } catch (NoSuchAlgorithmException e) {
433 throw new Exception(String.format("没有[%s]此类加密算法", cipherAlgorithm));
434 } catch (NoSuchPaddingException e) {
435 throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
436 } catch (InvalidKeyException e) {
437 throw new Exception("无效密钥");
438 } catch (IllegalBlockSizeException e) {
439 throw new Exception("加密块大小不合法");
440 } catch (BadPaddingException e) {
441 throw new Exception("错误填充模式");
442 } catch (IOException e) {
443 throw new Exception("字节输出流异常");
444 }
445 }
446
447 /**
448 * RSA解密
449 *
450 * @param encryptedBytes
451 * 加密后字节数组
452 * @param privateKey
453 * 私钥
454 * @param keyLength
455 * 密钥bit长度
456 * @param reserveSize
457 * padding填充字节数,预留11字节
458 * @param cipherAlgorithm
459 * 加解密算法,一般为RSA/ECB/PKCS1Padding
460 * @return 解密后字节数组,不经base64编码
461 * @throws Exception
462 */
463 public static byte[] RSADecrypt(byte[] encryptedBytes, PrivateKey privateKey, int keyLength, int reserveSize, String cipherAlgorithm)
464 throws Exception {
465 int keyByteSize = keyLength / 8; // 密钥字节数
466 int decryptBlockSize = keyByteSize - reserveSize; // 解密块大小=密钥字节数-padding填充字节数
467 int nBlock = encryptedBytes.length / keyByteSize;// 计算分段解密的block数,理论上能整除
468
469 try {
470 Cipher cipher = Cipher.getInstance(cipherAlgorithm);
471 cipher.init(Cipher.DECRYPT_MODE, privateKey);
472
473 // 输出buffer,大小为nBlock个decryptBlockSize
474 ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlockSize);
475 // 分段解密
476 for (int offset = 0; offset < encryptedBytes.length; offset += keyByteSize) {
477 // block大小: decryptBlock 或 剩余字节数
478 int inputLen = encryptedBytes.length - offset;
479 if (inputLen > keyByteSize) {
480 inputLen = keyByteSize;
481 }
482
483 // 得到分段解密结果
484 byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLen);
485 // 追加结果到输出buffer中
486 outbuf.write(decryptedBlock);
487 }
488
489 outbuf.flush();
490 outbuf.close();
491 return outbuf.toByteArray();
492 } catch (NoSuchAlgorithmException e) {
493 throw new Exception(String.format("没有[%s]此类解密算法", cipherAlgorithm));
494 } catch (NoSuchPaddingException e) {
495 throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
496 } catch (InvalidKeyException e) {
497 throw new Exception("无效密钥");
498 } catch (IllegalBlockSizeException e) {
499 throw new Exception("解密块大小不合法");
500 } catch (BadPaddingException e) {
501 throw new Exception("错误填充模式");
502 } catch (IOException e) {
503 throw new Exception("字节输出流异常");
504 }
505 }
506
507 /**
508 * AES加密
509 *
510 * @param plainBytes
511 * 明文字节数组
512 * @param keyBytes
513 * 密钥字节数组
514 * @param keyAlgorithm
515 * 密钥算法
516 * @param cipherAlgorithm
517 * 加解密算法
518 * @param IV
519 * 随机向量
520 * @return 加密后字节数组,不经base64编码
521 * @throws Exception
522 */
523 public static byte[] AESEncrypt(byte[] plainBytes, byte[] keyBytes, String keyAlgorithm, String cipherAlgorithm, String IV)
524 throws Exception {
525 try {
526 // AES密钥长度为128bit、192bit、256bit,默认为128bit
527 if (keyBytes.length % 8 != 0 || keyBytes.length < 16 || keyBytes.length > 32) {
528 throw new Exception("AES密钥长度不合法");
529 }
530
531 Cipher cipher = Cipher.getInstance(cipherAlgorithm);
532 SecretKey secretKey = new SecretKeySpec(keyBytes, keyAlgorithm);
533 if (StringUtils.trimToNull(IV) != null) {
534 IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes());
535 cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
536 } else {
537 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
538 }
539
540 byte[] encryptedBytes = cipher.doFinal(plainBytes);
541
542 return encryptedBytes;
543 } catch (NoSuchAlgorithmException e) {
544 throw new Exception(String.format("没有[%s]此类加密算法", cipherAlgorithm));
545 } catch (NoSuchPaddingException e) {
546 throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
547 } catch (InvalidKeyException e) {
548 throw new Exception("无效密钥");
549 } catch (InvalidAlgorithmParameterException e) {
550 throw new Exception("无效密钥参数");
551 } catch (BadPaddingException e) {
552 throw new Exception("错误填充模式");
553 } catch (IllegalBlockSizeException e) {
554 throw new Exception("加密块大小不合法");
555 }
556 }
557
558 /**
559 * AES解密
560 *
561 * @param encryptedBytes
562 * 密文字节数组,不经base64编码
563 * @param keyBytes
564 * 密钥字节数组
565 * @param keyAlgorithm
566 * 密钥算法
567 * @param cipherAlgorithm
568 * 加解密算法
569 * @param IV
570 * 随机向量
571 * @return 解密后字节数组
572 * @throws Exception
573 */
574 public static byte[] AESDecrypt(byte[] encryptedBytes, byte[] keyBytes, String keyAlgorithm, String cipherAlgorithm, String IV)
575 throws Exception {
576 try {
577 // AES密钥长度为128bit、192bit、256bit,默认为128bit
578 if (keyBytes.length % 8 != 0 || keyBytes.length < 16 || keyBytes.length > 32) {
579 throw new Exception("AES密钥长度不合法");
580 }
581
582 Cipher cipher = Cipher.getInstance(cipherAlgorithm);
583 SecretKey secretKey = new SecretKeySpec(keyBytes, keyAlgorithm);
584 if (IV != null && StringUtils.trimToNull(IV) != null) {
585 IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes());
586 cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
587 } else {
588 cipher.init(Cipher.DECRYPT_MODE, secretKey);
589 }
590
591 byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
592
593 return decryptedBytes;
594 } catch (NoSuchAlgorithmException e) {
595 throw new Exception(String.format("没有[%s]此类加密算法", cipherAlgorithm));
596 } catch (NoSuchPaddingException e) {
597 throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
598 } catch (InvalidKeyException e) {
599 throw new Exception("无效密钥");
600 } catch (InvalidAlgorithmParameterException e) {
601 throw new Exception("无效密钥参数");
602 } catch (BadPaddingException e) {
603 throw new Exception("错误填充模式");
604 } catch (IllegalBlockSizeException e) {
605 throw new Exception("解密块大小不合法");
606 }
607 }
608
609 public static void decode(String plain,String sign,String charset, PublicKey hzfPubKey) throws Exception{
610 byte []signData = KeyUtil.string2bytes(sign, 16);
611 byte[] signBytes = Base64.decodeBase64(signData);
612 // 使用商户公钥验证签名
613 boolean verifySign = CryptoUtil.verifyDigitalSign(plain.getBytes(charset), signBytes, hzfPubKey, "SHA1WithRSA");
614 if (verifySign) {
615 System.out.println("success");
616 }else{
617 System.out.println("failure");
618 }
619 }
620 public static void decode2(String plain,String sign,String charset, PublicKey hzfPubKey) throws Exception{
621 System.out.println(sign.length());
622 byte[] signBytes = Base64.decodeBase64(sign.getBytes(charset));
623 // 使用商户公钥验证签名
624 boolean verifySign = CryptoUtil.verifyDigitalSign(plain.getBytes(charset), signBytes, hzfPubKey, "SHA1WithRSA");
625 if (verifySign) {
626 System.out.println("success");
627 }else{
628 System.out.println("failure");
629 }
630 }
631 public static byte[] hexString2ByteArr(String hexStr) {
632 return new BigInteger(hexStr, 16).toByteArray();
633 }
634 public static final byte[] hexStrToBytes(String s) {
635 byte[] bytes;
636 bytes = new byte[s.length() / 2];
637 for (int i = 0; i < bytes.length; i++) {
638 bytes[i] = (byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2), 16);
639 }
640 return bytes;
641 }
642 /**
643 * 字符数组16进制字符
644 *
645 * @param bytes
646 * @return
647 */
648 public static String bytes2string(byte[] bytes, int radix) {
649 int size = 2;
650 if (radix == 2) {
651 size = 8;
652 }
653 StringBuilder sb = new StringBuilder(bytes.length * size);
654 for (int i = 0; i < bytes.length; i++) {
655 int integer = bytes[i];
656 while (integer < 0) {
657 integer = integer + 256;
658 }
659 String str = Integer.toString(integer, radix);
660 sb.append(StringUtils.leftPad(str.toUpperCase(), size, "0"));
661 }
662 return sb.toString();
663 }
664
665 public static void main(String[] args) throws Exception {
666 PublicKey publicKey = null;
667 PrivateKey privateKey = null;
668 //获取私钥
669 privateKey = CryptoUtil.getRSAPrivateKeyByFileSuffix("D://cert//private.pem","pem", null, "RSA");
670 //获取公钥
671 publicKey = CryptoUtil.getRSAPublicKeyByFileSuffix("D://cert//public.pem", "pem", "RSA");
672 System.out.println("读取的私钥:"+privateKey);
673 System.out.println("读取的公钥:"+publicKey);
674 }
675 }
控制台输出结果:
读取的私钥:sun.security.rsa.RSAPrivateCrtKeyImpl@fff05816
读取的公钥:Sun RSA public key, 2048 bits
modulus: 30352155361112247049303023420825968441063511556566487358373956485006944773551398257671324487561388937350982933590996006918917835477306783114880261383096591901729604794449957707356662262038622235714415672319014898606436696498375107689676577265348959122388532382235612467368418451329595782103970659911528392163885786170603134755166468968487939620387819720026031694626892021929646702745041404121725278045217747484135218828125738622519067039814579552154369394886130771836587225024718009130734917582986728907040272962770508323361317551681311374372344275619882074968240026190384773026535791888298002884088455001552303871327
public exponent: 65537
参考:
1、http://blog.csdn.net/zhiyuan411/article/details/39100241
2、https://baike.baidu.com/item/%E6%95%B0%E5%AD%97%E8%AF%81%E4%B9%A6(百度百科,数字证书相关介绍,个人感觉挺好,挺详细的)