package com.xxx.util; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.*; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.bouncycastle.asn1.DEROutputStream; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.*; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.Store; /** * PKCS7Tool.java pkcs7格式簽名工具,讀取證書密鑰進行加密 * * @version 1.0 * @author Written Date: 2020-01-03 * JDK */ public class PKCS7Tool { /** 簽名證書鏈 */ static X509Certificate[] certificates = null; /** 簽名私鑰 */ static PrivateKey privateKey = null; /** 根證書 */ //private Certificate rootCertificate = null; /** 證書存放路徑 */ static String keyStorePath = "C:\\upload\\cert.pfx"; /** 證書密碼 */ static String keyStorePassword = ".."; /** JVM 提供商 */ private static char jvm = 0; private static Class algorithmId = null; private static Class derValue = null; private static Class objectIdentifier = null; private static Class x500Name = null; private static boolean debug = false; private static BouncyCastleProvider bouncyCastleProvider = null; static { try { //如果是PKCS7Padding填充方式,則必須加上下面這行 if (bouncyCastleProvider == null) { bouncyCastleProvider = new BouncyCastleProvider(); } Security.addProvider(bouncyCastleProvider); init(); // 加載證書庫 KeyStore keyStore = null; if (keyStorePath.toLowerCase().endsWith(".pfx")) keyStore = KeyStore.getInstance("PKCS12"); else keyStore = KeyStore.getInstance("JKS"); FileInputStream fis = null; try { fis = new FileInputStream(keyStorePath); keyStore.load(fis, keyStorePassword.toCharArray()); } finally { if (fis != null) fis.close(); } // 在證書庫中找到簽名私鑰 Enumeration aliases = keyStore.aliases(); String keyAlias = null; if (aliases != null) { while (aliases.hasMoreElements()) { keyAlias = (String) aliases.nextElement(); Certificate[] certs = keyStore.getCertificateChain(keyAlias); if (certs == null || certs.length == 0) continue; X509Certificate cert = (X509Certificate) certs[0]; if (matchUsage(cert.getKeyUsage(), 1)) { try { cert.checkValidity(); } catch (CertificateException e) { continue; } break; } } } // 沒有找到可用簽名私鑰 if (keyAlias == null) throw new GeneralSecurityException( "None certificate for sign in this keystore"); if (debug) System.out.println(keyAlias); if (keyStore.isKeyEntry(keyAlias)) { // 檢查證書鏈 Certificate[] certs = keyStore.getCertificateChain(keyAlias); for (int i = 0; i < certs.length; i++) { if (!(certs[i] instanceof X509Certificate)) throw new GeneralSecurityException("Certificate[" + i + "] in chain '" + keyAlias + "' is not a X509Certificate."); } // 轉換證書鏈 certificates = new X509Certificate[certs.length]; for (int i = 0; i < certs.length; i++) certificates[i] = (X509Certificate) certs[i]; } else if (keyStore.isCertificateEntry(keyAlias)) { // 只有單張證書 Certificate cert = keyStore.getCertificate(keyAlias); if (cert instanceof X509Certificate) { certificates = new X509Certificate[ { (X509Certificate) cert }]; } } else { throw new GeneralSecurityException(keyAlias + " is unknown to this keystore"); } privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyStorePassword.toCharArray()); // 沒有私鑰拋異常 if (privateKey == null) { throw new GeneralSecurityException(keyAlias + " could not be accessed"); } } catch (Exception e) { e.printStackTrace(); } } /** * 簽名 * * @param data * 數據 * @return signature 簽名結果 * @throws GeneralSecurityException * @throws IOException * @throws IllegalArgumentException */ public static String sign(byte[] data) throws Exception { X509Certificate cerx509 = certificates[0]; List<X509Certificate> certList = new ArrayList<X509Certificate>(); certList.add(cerx509); Store certs = new JcaCertStore(certList); ContentSigner contentSigner = new JcaContentSignerBuilder(cerx509.getSigAlgName()).setProvider("BC").build(privateKey); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder( new JcaDigestCalculatorProviderBuilder().setProvider("BC") .build()).setDirectSignature(true).build(contentSigner, cerx509)); gen.addCertificates(certs); CMSTypedData msg = new CMSProcessableByteArray(data); CMSSignedData sigData = gen.generate(msg, false); org.bouncycastle.asn1.cms.ContentInfo a = sigData.toASN1Structure(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try{ DEROutputStream dOut = new DEROutputStream(bOut); dOut.writeObject(a); return Base64.encodeBase64String(bOut.toByteArray()); }finally{ IOUtils.closeQuietly(bOut); } } /** * 驗證簽名(無CRL) * * @param data * 被簽名數據 * @param signature * 簽名簽名結果 * @throws Exception */ public static boolean verify(byte[] data, byte[] signature) throws Exception { CMSProcessable content = new CMSProcessableByteArray(data); //新建PKCS#7簽名數據處理對象 CMSSignedData s = new CMSSignedData(content, signature); Store certStore = s.getCertificates(); //獲得簽名者信息 SignerInformationStore signers = s.getSignerInfos(); Iterator<?> it = signers.getSigners().iterator(); int verified = 0, size = 0; //當有多個簽名者信息時需要全部驗證 while (it.hasNext()) { size++; SignerInformation signer = (SignerInformation) it.next(); //證書鏈 Collection<?> certCollection = certStore.getMatches(signer.getSID()); Iterator<?> certIt = certCollection.iterator(); X509CertificateHolder cert = (X509CertificateHolder) certIt.next(); //驗證數字簽名 if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) { verified++; } } if (verified >0 && size == verified) { return true; } return false; } /** * 匹配私鑰用法 * * @param keyUsage * @param usage * @return */ private static boolean matchUsage(boolean[] keyUsage, int usage) { if (usage == 0 || keyUsage == null) return true; for (int i = 0; i < Math.min(keyUsage.length, 32); i++) { if ((usage & (1 << i)) != 0 && !keyUsage[i]) return false; } return true; } private static void init() { if (jvm != 0) return; String vendor = System.getProperty("java.vm.vendor"); if (vendor == null) vendor = ""; String vendorUC = vendor.toUpperCase(); try { if (vendorUC.indexOf("SUN") >= 0) { jvm = 'S'; algorithmId = Class.forName("sun.security.x509.AlgorithmId"); derValue = Class.forName("sun.security.util.DerValue"); objectIdentifier = Class.forName("sun.security.util.ObjectIdentifier"); x500Name = Class.forName("sun.security.x509.X500Name"); } else if (vendorUC.indexOf("ORACLE CORPORATION") >= 0) { jvm = 'O'; algorithmId = Class.forName("sun.security.x509.AlgorithmId"); derValue = Class.forName("sun.security.util.DerValue"); objectIdentifier = Class.forName("sun.security.util.ObjectIdentifier"); x500Name = Class.forName("sun.security.x509.X500Name"); } else if (vendorUC.indexOf("IBM") >= 0) { jvm = 'I'; algorithmId = Class.forName("com.ibm.security.x509.AlgorithmId"); derValue = Class.forName("com.ibm.security.util.DerValue"); objectIdentifier = Class.forName("com.ibm.security.util.ObjectIdentifier"); x500Name = Class.forName("com.ibm.security.x509.X500Name"); } else { System.out.println("Not support JRE: " + vendor); System.exit(-1); } } catch (ClassNotFoundException e) { e.printStackTrace(); System.exit(-1); } } }
相關jar包:compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.52'