常见数字证书类型
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)的区别
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 * <strong>Title : CryptoUtil</strong><br> 57 * <strong>Description : 加解密工具类</strong><br> 58 * <strong>Create on : 2015-05-04</strong><br> 59 * 60 * @author linshangqing@cmbc.com.cn<br> 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 * <br>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<String> aliasenum = ks.aliases(); 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