|
@@ -1,459 +0,0 @@
|
|
|
-package com.xynet.pay.util;
|
|
|
-
|
|
|
-import sun.misc.BASE64Decoder;
|
|
|
-import sun.misc.BASE64Encoder;
|
|
|
-import sun.security.pkcs.ContentInfo;
|
|
|
-import sun.security.pkcs.PKCS7;
|
|
|
-import sun.security.pkcs.PKCS9Attributes;
|
|
|
-import sun.security.pkcs.SignerInfo;
|
|
|
-
|
|
|
-import javax.security.auth.x500.X500Principal;
|
|
|
-import java.io.*;
|
|
|
-import java.lang.reflect.Array;
|
|
|
-import java.lang.reflect.Constructor;
|
|
|
-import java.lang.reflect.Field;
|
|
|
-import java.lang.reflect.Method;
|
|
|
-import java.math.BigInteger;
|
|
|
-import java.security.*;
|
|
|
-import java.security.cert.Certificate;
|
|
|
-import java.security.cert.CertificateException;
|
|
|
-import java.security.cert.CertificateFactory;
|
|
|
-import java.security.cert.X509Certificate;
|
|
|
-import java.util.Enumeration;
|
|
|
-
|
|
|
-public class PKCS7Tool {
|
|
|
-
|
|
|
- /** 签名 */
|
|
|
- private static final int SIGNER = 1;
|
|
|
- /** 验证 */
|
|
|
- private static final int VERIFIER = 2;
|
|
|
- /** 用途 */
|
|
|
- private int mode = 0;
|
|
|
- /** 摘要算法 */
|
|
|
- private static String digestAlgorithm = "SHA1";
|
|
|
- /** 签名算法 */
|
|
|
- private static String signingAlgorithm = "SHA1withRSA";
|
|
|
- /** 签名证书 */
|
|
|
- private X509Certificate[] certificates = null;
|
|
|
- /** 签名私钥 */
|
|
|
- private PrivateKey privateKey = null;
|
|
|
- /** 根证书 */
|
|
|
- private Certificate[] rootCertificates = null;
|
|
|
- /** 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 PKCS7Tool(int mode) {
|
|
|
- this.mode = mode;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 匹配私钥用法
|
|
|
- *
|
|
|
- * @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("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 {
|
|
|
- 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");
|
|
|
- }
|
|
|
- } catch (ClassNotFoundException e) {
|
|
|
- e.printStackTrace();
|
|
|
- System.out.println("Not support JRE: " + vendor);
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取得签名工具 加载证书库, 取得签名证书链和私钥
|
|
|
- *
|
|
|
- * @param keyStorePath
|
|
|
- * 证书库路径
|
|
|
- * @param keyStorePassword
|
|
|
- * 证书库口令
|
|
|
- * @param keyPassword
|
|
|
- * 证书口令
|
|
|
- * @throws GeneralSecurityException
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- public static PKCS7Tool getSigner(String keyStorePath,
|
|
|
- String keyStorePassword, String keyPassword)
|
|
|
- throws GeneralSecurityException, IOException {
|
|
|
- String type;
|
|
|
- if (keyStorePath.toLowerCase().endsWith(".pfx"))
|
|
|
- type = "PKCS12";
|
|
|
- else if (keyStorePath.toLowerCase().endsWith(".jks"))
|
|
|
- type = "JKS";
|
|
|
- else
|
|
|
- throw new IllegalStateException("Unknown keystore type.");
|
|
|
- FileInputStream fis = null;
|
|
|
- try {
|
|
|
- fis = new FileInputStream(keyStorePath);
|
|
|
- return getSigner(fis, type, keyStorePassword, keyPassword);
|
|
|
- } finally {
|
|
|
- if (fis != null)
|
|
|
- fis.close();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取得签名工具 加载证书库, 取得签名证书链和私钥
|
|
|
- *
|
|
|
- * @param keyStorePath
|
|
|
- * 证书库路径
|
|
|
- * @param keyStorePassword
|
|
|
- * 证书库口令
|
|
|
- * @param keyPassword
|
|
|
- * 证书口令
|
|
|
- * @throws GeneralSecurityException
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- public static PKCS7Tool getSigner(InputStream keyStoreStream, String type,
|
|
|
- String keyStorePassword, String keyPassword)
|
|
|
- throws GeneralSecurityException, IOException {
|
|
|
- init();
|
|
|
- // 加载证书库
|
|
|
- KeyStore keyStore = KeyStore.getInstance(type);
|
|
|
- keyStore.load(keyStoreStream, keyStorePassword.toCharArray());
|
|
|
- // 在证书库中找到签名私钥
|
|
|
- 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);
|
|
|
- System.out.println("SIGNER =\n"
|
|
|
- + new BASE64Encoder().encode(keyStore.getCertificate(
|
|
|
- keyAlias).getEncoded()));
|
|
|
- }
|
|
|
- // 签名证书只能放一张
|
|
|
- X509Certificate certificate = null;
|
|
|
- if (keyStore.isKeyEntry(keyAlias)
|
|
|
- || keyStore.isCertificateEntry(keyAlias)) {
|
|
|
- certificate = (X509Certificate) keyStore.getCertificate(keyAlias);
|
|
|
- } else {
|
|
|
- throw new GeneralSecurityException(keyAlias
|
|
|
- + " is unknown to this keystore");
|
|
|
- }
|
|
|
-
|
|
|
- PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias,
|
|
|
- keyPassword.toCharArray());
|
|
|
- // 没有私钥抛异常
|
|
|
- if (privateKey == null) {
|
|
|
- throw new GeneralSecurityException(keyAlias
|
|
|
- + " could not be accessed");
|
|
|
- }
|
|
|
-
|
|
|
- PKCS7Tool tool = new PKCS7Tool(SIGNER);
|
|
|
- tool.certificates = new X509Certificate[] { certificate };
|
|
|
- tool.privateKey = privateKey;
|
|
|
- return tool;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取得验签名工具 加载信任根证书
|
|
|
- *
|
|
|
- * @param rootCertificatePath
|
|
|
- * 根证书路径
|
|
|
- * @throws GeneralSecurityException
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- public static PKCS7Tool getVerifier(String rootCertificatePath)
|
|
|
- throws GeneralSecurityException, IOException {
|
|
|
- String type;
|
|
|
- if (rootCertificatePath.toLowerCase().endsWith(".p7b"))
|
|
|
- type = "PKCS7";
|
|
|
- else if (rootCertificatePath.toLowerCase().endsWith(".cer"))
|
|
|
- type = "DER";
|
|
|
- else
|
|
|
- throw new IllegalStateException("Unknown rootstore type.");
|
|
|
- FileInputStream fis = null;
|
|
|
- try {
|
|
|
- fis = new FileInputStream(rootCertificatePath);
|
|
|
- return getVerifier(fis, type);
|
|
|
- } finally {
|
|
|
- if (fis != null)
|
|
|
- fis.close();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取得验签名工具 加载信任根证书
|
|
|
- *
|
|
|
- * @param rootCertStresm
|
|
|
- * 根证书输入流
|
|
|
- * @throws GeneralSecurityException
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- public static PKCS7Tool getVerifier(InputStream rootCertStream, String type)
|
|
|
- throws GeneralSecurityException, IOException {
|
|
|
- init();
|
|
|
- // 加载根证书
|
|
|
- Certificate[] rootCertificates = null;
|
|
|
-
|
|
|
- CertificateFactory certificatefactory = CertificateFactory
|
|
|
- .getInstance("X.509");
|
|
|
- if ("DER".equals(type)) {
|
|
|
- try {
|
|
|
- rootCertificates = new Certificate[] { certificatefactory
|
|
|
- .generateCertificate(rootCertStream) };
|
|
|
- } catch (Exception exception) {
|
|
|
- if (debug)
|
|
|
- exception.printStackTrace();
|
|
|
- InputStream is = new ByteArrayInputStream(
|
|
|
- new BASE64Decoder().decodeBuffer(rootCertStream));
|
|
|
- rootCertificates = new Certificate[] { certificatefactory
|
|
|
- .generateCertificate(is) };
|
|
|
- }
|
|
|
- } else if ("PKCS7".equals(type)) {
|
|
|
- PKCS7 p7;
|
|
|
- byte[] roots = readData(rootCertStream);
|
|
|
- try {
|
|
|
- p7 = new PKCS7(roots);
|
|
|
- } catch (Exception exception) {
|
|
|
- roots = new BASE64Decoder().decodeBuffer(new String(roots));
|
|
|
- p7 = new PKCS7(roots);
|
|
|
- }
|
|
|
- rootCertificates = p7.getCertificates();
|
|
|
- } else
|
|
|
- throw new IllegalStateException("Unknown root certificate(s) type.");
|
|
|
-
|
|
|
- PKCS7Tool tool = new PKCS7Tool(VERIFIER);
|
|
|
- tool.rootCertificates = rootCertificates;
|
|
|
- return tool;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 签名
|
|
|
- *
|
|
|
- * @param data
|
|
|
- * 数据
|
|
|
- * @return signature 签名结果
|
|
|
- * @throws GeneralSecurityException
|
|
|
- * @throws IOException
|
|
|
- * @throws IllegalArgumentException
|
|
|
- */
|
|
|
- public String sign(byte[] data) throws Exception {
|
|
|
- if (mode != SIGNER)
|
|
|
- throw new IllegalStateException(
|
|
|
- "call a PKCS7Tool instance not for signature.");
|
|
|
-
|
|
|
- Signature signer = Signature.getInstance(signingAlgorithm);
|
|
|
- signer.initSign(privateKey);
|
|
|
- signer.update(data, 0, data.length);
|
|
|
- byte[] signedAttributes = signer.sign();
|
|
|
-
|
|
|
- ContentInfo contentInfo = null;
|
|
|
- Field data_oidField = ContentInfo.class.getField("DATA_OID");
|
|
|
- Object data_oid = data_oidField.get(null);
|
|
|
- Constructor<ContentInfo> contentInfoConstructor = ContentInfo.class
|
|
|
- .getConstructor(new Class[] { data_oid.getClass(), derValue });
|
|
|
- contentInfo = contentInfoConstructor
|
|
|
- .newInstance(new Object[] { data_oid, null });
|
|
|
- // 根证书
|
|
|
- X509Certificate x509 = certificates[certificates.length - 1];
|
|
|
- BigInteger serial = x509.getSerialNumber();
|
|
|
- // X500Name
|
|
|
- Constructor x500NameConstructor = x500Name
|
|
|
- .getConstructor(new Class[] { String.class });
|
|
|
- Object x500NameObject = x500NameConstructor
|
|
|
- .newInstance(new Object[] { x509.getIssuerDN().getName() });
|
|
|
- // AlgorithmId
|
|
|
- Method algorithmIdGet = algorithmId.getMethod("get",
|
|
|
- new Class[] { String.class });
|
|
|
- Object digestAlgorithmId = algorithmIdGet.invoke(null,
|
|
|
- new Object[] { digestAlgorithm });
|
|
|
- Field algorithmIdfield = algorithmId.getField("RSAEncryption_oid");
|
|
|
- Object rsaOid = algorithmIdfield.get(null);
|
|
|
- Constructor algorithmConstructor = algorithmId
|
|
|
- .getConstructor(new Class[] { objectIdentifier });
|
|
|
- Object algorithmRsaOid = algorithmConstructor
|
|
|
- .newInstance(new Object[] { rsaOid });
|
|
|
- // SignerInfo
|
|
|
- Constructor<SignerInfo> signerInfoConstructor = SignerInfo.class
|
|
|
- .getConstructor(new Class[] { x500Name, BigInteger.class,
|
|
|
- algorithmId, PKCS9Attributes.class, algorithmId,
|
|
|
- byte[].class, PKCS9Attributes.class });
|
|
|
- // 签名信息
|
|
|
- SignerInfo si = signerInfoConstructor
|
|
|
- .newInstance(new Object[] { x500NameObject,// X500Name,
|
|
|
- // issuerName,
|
|
|
- serial, // x509.getSerialNumber(), BigInteger serial,
|
|
|
- digestAlgorithmId, // AlgorithmId, digestAlgorithmId,
|
|
|
- null, // PKCS9Attributes, authenticatedAttributes,
|
|
|
- algorithmRsaOid, // AlgorithmId,
|
|
|
- // digestEncryptionAlgorithmId,
|
|
|
- signedAttributes, // byte[] encryptedDigest,
|
|
|
- null // PKCS9Attributes unauthenticatedAttributes)
|
|
|
- });
|
|
|
-
|
|
|
- SignerInfo[] signerInfos = { si };
|
|
|
-
|
|
|
- // 构造PKCS7数据
|
|
|
- Object digestAlgorithmIds = Array.newInstance(algorithmId, 1);
|
|
|
- Array.set(digestAlgorithmIds, 0, digestAlgorithmId);
|
|
|
- // PKCS7
|
|
|
- Constructor<PKCS7> pkcs7Constructor = PKCS7.class.getConstructor(new Class[] {
|
|
|
- digestAlgorithmIds.getClass(), ContentInfo.class,
|
|
|
- X509Certificate[].class, signerInfos.getClass() });
|
|
|
- PKCS7 p7 = pkcs7Constructor.newInstance(new Object[] {
|
|
|
- digestAlgorithmIds, contentInfo, certificates, signerInfos });
|
|
|
-
|
|
|
- ByteArrayOutputStream baout = new ByteArrayOutputStream();
|
|
|
- p7.encodeSignedData(baout);
|
|
|
- // Base64编码
|
|
|
- return (new BASE64Encoder()).encode(baout.toByteArray());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 验证签名(无CRL)
|
|
|
- *
|
|
|
- * @param signature
|
|
|
- * 签名签名结果
|
|
|
- * @param data
|
|
|
- * 被签名数据
|
|
|
- * @param dn
|
|
|
- * 签名证书dn, 如果为空则不做匹配验证
|
|
|
- * @throws IOException
|
|
|
- * @throws NoSuchAlgorithmException
|
|
|
- * @throws SignatureException
|
|
|
- * @throws InvalidKeyException
|
|
|
- * @throws CertificateException
|
|
|
- * @throws NoSuchProviderException
|
|
|
- */
|
|
|
- public void verify(String signature, byte[] data, String dn)
|
|
|
- throws IOException, NoSuchAlgorithmException, SignatureException,
|
|
|
- InvalidKeyException, CertificateException, NoSuchProviderException {
|
|
|
- if (mode != VERIFIER)
|
|
|
- throw new IllegalStateException(
|
|
|
- "call a PKCS7Tool instance not for verify.");
|
|
|
- byte[] sign = new BASE64Decoder().decodeBuffer(signature);
|
|
|
- PKCS7 p7 = new PKCS7(sign);
|
|
|
- X509Certificate[] certs = p7.getCertificates();
|
|
|
- if (debug)
|
|
|
- for (int i = 0; i < certs.length; i++) {
|
|
|
- X509Certificate cert = certs[i];
|
|
|
- System.out.println("SIGNER " + i + "=\n" + cert);
|
|
|
- System.out.println("SIGNER " + i + "=\n"
|
|
|
- + new BASE64Encoder().encode(cert.getEncoded()));
|
|
|
- }
|
|
|
-
|
|
|
- // 验证签名本身、证书用法、证书扩展
|
|
|
- SignerInfo[] sis = p7.verify(data);
|
|
|
-
|
|
|
- // check the results of the verification
|
|
|
- if (sis == null)
|
|
|
- throw new SignatureException(
|
|
|
- "Signature failed verification, data has been tampered");
|
|
|
-
|
|
|
- for (int i = 0; i < sis.length; i++) {
|
|
|
- SignerInfo si = sis[i];
|
|
|
-
|
|
|
- X509Certificate cert = si.getCertificate(p7);
|
|
|
- // 证书是否过期验证,如果不用系统日期可用cert.checkValidity(date);
|
|
|
- cert.checkValidity();
|
|
|
- boolean valid = false;
|
|
|
- for (int j = rootCertificates.length - 1; j >= 0; j--) {
|
|
|
- if (cert.equals(rootCertificates[j])) {
|
|
|
- valid = true;
|
|
|
- break;
|
|
|
- } else {
|
|
|
- // 验证证书签名
|
|
|
- try {
|
|
|
- cert.verify(rootCertificates[j].getPublicKey());
|
|
|
- valid = true;
|
|
|
- break;
|
|
|
- } catch (Exception e) {
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (!valid)
|
|
|
- throw new SignatureException(
|
|
|
- "Signature certificate's issuer is untrusty.");
|
|
|
- // 验证dn
|
|
|
- if (i == 0 && dn != null) {
|
|
|
- X500Principal name = cert.getSubjectX500Principal();
|
|
|
- if (!dn.equals(name.getName(X500Principal.RFC1779))
|
|
|
- && !new X500Principal(dn).equals(name))
|
|
|
- throw new SignatureException("Signer dn '"
|
|
|
- + name.getName(X500Principal.RFC1779)
|
|
|
- + "' does not matchs '" + dn + "'");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 读取输入流
|
|
|
- *
|
|
|
- * @param in
|
|
|
- * 输入流
|
|
|
- * @return 数据
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- protected static byte[] readData(InputStream in) throws IOException {
|
|
|
- ByteArrayOutputStream bai = new ByteArrayOutputStream();
|
|
|
- byte[] buf = new byte[256];
|
|
|
- int len;
|
|
|
- while ((len = in.read(buf)) > 0)
|
|
|
- bai.write(buf, 0, len);
|
|
|
- return bai.toByteArray();
|
|
|
- }
|
|
|
-}
|