证书处理工具类

开发CDN平台过程,涉及到需要对证书做有效性验证,开发了以下工具类接口

   

package hs.cdn.tool;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

import ms.core.tool.DateTimeTool;
import ms.core.tool.JsonTool;
import ms.core.tool.SysTool;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.crypto.Cipher;
import javax.security.cert.X509Certificate;

import java.io.*;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * openssl 证书公钥私钥读取加密解密工具类,主要用于验证上传的 openssl生成的证书和私钥文件是否正确的问题
 * 主要逻辑:
 * 1、根据私钥文件读取私钥
 * 2、根据公钥文件读取公钥
 * 3、根据证书文件读取公钥
 * 4、根据证书公钥加密字符串
 * 5、根据证书私钥解密字符串
 * 6、如果字符串经过证书公钥加密后,再根据证书私钥解密后能后还原,说明上传的证书和私钥是正确的
 */
public class OpenSslUtils {
    private static final String DEFAULT_ENCODING = "UTF-8";

    private static final Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_ENCODING);

    /** 默认是RSA/NONE/PKCS1Padding */
    private static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";

    /** RSA密钥长度必须是64的倍数,在512~65536之间。默认是1024 */
    private static final int KEY_SIZE = 1024;

    /** RSA最大加密明文大小:明文长度(bytes) <= 密钥长度(bytes)-11 */
    private static final int MAX_ENCRYPT_BLOCK = KEY_SIZE / 8 - 11;

    /** RSA最大解密密文大小 */
    private static final int MAX_DECRYPT_BLOCK = KEY_SIZE / 8;

    private static Logger logger = LogManager.getLogger(OpenSslUtils.class);
    
    /**
     * 利用开源的工具类解析openssl私钥,openssl私钥文件格式为pem,
     * 需要去除页眉页脚后才能被程序识别
     * @param txtKey
     * @return
     */
    public static PrivateKey getPrivateKey(String txtKey) {
        PrivateKey privKey = null;
        PemReader pemReader = null;
        try {
            pemReader = new PemReader(new StringReader(txtKey));
            PemObject pemObject = pemReader.readPemObject();
            byte[] pemContent = pemObject.getContent();
            //支持从PKCS#1或PKCS#8 格式的私钥文件中提取私钥
            if (pemObject.getType().endsWith("RSA PRIVATE KEY")) {
               //取得私钥  for PKCS#1
               RSAPrivateKey asn1PrivKey = RSAPrivateKey.getInstance(pemContent);
               RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());  
               KeyFactory keyFactory= KeyFactory.getInstance("RSA");
               privKey= keyFactory.generatePrivate(rsaPrivKeySpec); 
            } else if (pemObject.getType().endsWith("PRIVATE KEY")) {
                //取得私钥 for PKCS#8
                PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(pemContent);
                 KeyFactory kf = KeyFactory.getInstance("RSA");
                 privKey = kf.generatePrivate(privKeySpec);
            }
        } catch (Exception e) {
            logger.error(e, e);
        } 
        
		try {
			if (pemReader != null) pemReader.close();
		} catch (IOException e) {
		}
		return privKey;
    }

    /**
     * 从key文件读取私钥内容,并获取key对象
     * @param file
     * @return
     */
    public static PrivateKey getPrivateKey(File file) {
        if (file == null || !file.exists()) return null;
        
        return getPrivateKey(SysTool.readTxtFile2Str(file, "UTF8"));
    }

    /**
     * 从证书文本获取公钥
     * @param content
     * @return
     */
    public static PublicKey getPublicKey(String txtKey) {
    	InputStream in = null;
    	PublicKey publicKey = null;
    	try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            in = new ByteArrayInputStream(txtKey.getBytes());
            Certificate crt = cf.generateCertificate(in);
            publicKey = crt.getPublicKey();
        } catch (Exception e) {
            logger.error(e, e);
        }

    	try {
    		if (in!=null) in.close();
    	} catch(Exception e) {    		
    	}
    	
        return publicKey;
    }

    /**
     * 从证书公钥内容串,提取证书信息
     * @param txtKey
     * @return
     */
    public static Map<String, Object> getPublicKeyInfo(String txtKey){
    	Map<String, Object> ret = new HashMap<String, Object>();
    	InputStream in = null;
    	try {
            in = new ByteArrayInputStream(txtKey.getBytes());
            X509Certificate crt = X509Certificate.getInstance(in);
            ret.put("expiry", crt.getNotAfter());
            ret.put("start", crt.getNotBefore());
            ret.put("issue", crt.getIssuerDN().getName());
            ret.put("ver", crt.getVersion());
        } catch (Exception e) {
            logger.error(e, e);
        }

    	try {
    		if (in!=null) in.close();
    	} catch(Exception e) {    		
    	}

        return ret;
    }

    /**
     * 从证书文件获取公钥
     *
     * @param file
     * @return
     */
    public static PublicKey getPublicKey(File file) {
    	if (file==null || !file.exists()) return null;
    	
    	return getPublicKey(SysTool.readTxtFile2Str(file, "UTF8"));
    }

    /**
     * 从openssl公钥文件中读取公钥
     * @param file
     * @return
     */
	public static PublicKey getPublicKeyFromFile(File file) {
		if (file == null) return null;

		PublicKey pubKey = null;
		PemReader pemReader = null;
		try {
			pemReader = new PemReader(new FileReader(file));
			PemObject pemObject = pemReader.readPemObject();
			byte[] pemContent = pemObject.getContent();
			//公钥需要使用x509格式编码
			X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(pemContent);
			KeyFactory kf = KeyFactory.getInstance("RSA");
			pubKey = kf.generatePublic(pubKeySpec);
		} catch (Exception e) {
			logger.error(e, e);
		}

		try {
			if (pemReader != null) pemReader.close();
		} catch (Exception ex) {
		}
		return pubKey;
	}

    /**
     * 公钥加密
     * @param key
     * @param plainBytes
     * @return
     */
    private static byte[] encrypt(PublicKey key, byte[] plainBytes) {
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key);

            if (plainBytes.length <= MAX_ENCRYPT_BLOCK) {
                return cipher.doFinal(plainBytes);
            }

            return cipher.doFinal(plainBytes);
        } catch (Exception e) {
            logger.error(e,e);
            return null;
        }
    }

    /**
     * 根据公钥加密字符串
     * @param key
     * @param plainText 需要加密的字符串
     * @return
     */
    public static String encrypt(PublicKey key, String plainText) {
        byte[] encodeBytes = encrypt(key, plainText.getBytes(DEFAULT_CHARSET));
        return Base64.encodeBase64String(encodeBytes);
    }

    /**
     * 私钥解密
     * @param key
     * @param encodedText
     * @return
     */
    private static String decrypt(PrivateKey key, byte[] encodedText) {
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, key);
            int inputLen = encodedText.length;

            if (inputLen <= MAX_DECRYPT_BLOCK) {
                return new String(cipher.doFinal(encodedText), DEFAULT_CHARSET);
            }

            byte[] cache = cipher.doFinal(encodedText);
            return new String(cache);
        } catch (Exception e) {
            logger.error(e,e);
            return null;
        }
    }

    /**
     * 根据私钥解密加密过的字符串
     * @param key
     * @param encodedText 加密过的字符串
     * @return 解密后的字符串
     */
    public static String decrypt(PrivateKey key, String encodedText) {
        byte[] bytes = Base64.decodeBase64(encodedText);
        return decrypt(key, bytes);
    }

    /**
     * 验证证书
     * @param cert
     * @return
     */
    public static String validateCert(File cert){
        if (cert == null) return "证书CRT文件不能为空";

        PublicKey publicKey = getPublicKey(cert);
        if (publicKey == null) return "无法读取证书公钥,证书CRT文件格式错误";

        return null;
    }

    /**
     * 验证私钥
     * @param privateKey
     * @return
     */
    public static String validatePrivateKey(File privateKey){
        if (privateKey == null) return "证书私钥不能为空";

        PrivateKey privKey = getPrivateKey(privateKey);
        if (privKey == null) return "无法读取证书私钥,证书私钥文件格式错误";
        
        return null;
    }

    /**
     * 验证证书私钥是否匹配,如果不匹配返回错误消息
     * @param cert
     * @param privateKey
     * @return 错误消息
     */
    public static String validate(File cert, File privateKey) {
        String res = validateCert(cert);		//验证证书
        if((res!=null)&&(res.length()>0)){
            return res;							//返回错误消息
        }
        res = validatePrivateKey(privateKey);	//验证私钥
        if((res!=null)&&(res.length()>0)){
            return res;							//返回错误消息
        }
        PublicKey publicKey = getPublicKey(cert);
        PrivateKey privKey = getPrivateKey(privateKey);
        String str = "test";					//测试字符串
        String encryptStr = OpenSslUtils.encrypt(publicKey, str);		//根据证书公钥对字符串进行加密
        String decryptStr = OpenSslUtils.decrypt(privKey, encryptStr);	//根据证书私钥对加密字符串进行解密
        if(!str.equals(decryptStr)){									//字符串根据证书公钥加密,私钥解密后不能还原说明证书与私钥不匹配
            return "证书与私钥不匹配";
        }
        return "success";
    }
    
    /**
     * 验证公私钥是否匹配
     * @param txtPrivateKey 私钥串
     * @param txtPublicKey 公钥串
     * @return 0-匹配;1-私钥错误;2-公钥错误;3-不匹配
     */
    public static int keyMatched(String txtPrivateKey, String txtPublicKey) {
    	PrivateKey prvKey = OpenSslUtils.getPrivateKey(txtPrivateKey);
    	if (prvKey==null) return 1;
    	
    	PublicKey pubKey = OpenSslUtils.getPublicKey(txtPublicKey);
    	if (pubKey==null) return 2;
    	
    	String str = "public key & private key";
        String encryptStr = encrypt(pubKey, str);
        String decryptStr = decrypt(prvKey, encryptStr);
        return str.equals(decryptStr) ? 0:3;
    }

    public static void main(String[] args) {
        File privateKeyFile = new File("d:/4key.txt");
        PrivateKey privKey = OpenSslUtils.getPrivateKey(privateKeyFile);
        System.out.println("私钥:" + privKey);

        File certFile = new File("d:/4.txt");
        PublicKey publicKey = OpenSslUtils.getPublicKey(certFile);
        System.out.println("公钥:" + publicKey);

        //公私钥匹配验证
        String validateResult = validate(certFile,privateKeyFile);
        System.out.println("匹配结果:" + validateResult);

        //加密
        String str = "this is a test";
        String encryptStr = OpenSslUtils.encrypt(publicKey, str);
        System.out.println("密文:" + encryptStr);

        //解密
        String decryptStr = OpenSslUtils.decrypt(privKey, encryptStr);
        System.out.println("明文:" + decryptStr);
        
        //获取证书信息,如有效期等
        Map<String, Object> map = getPublicKeyInfo(SysTool.readTxtFile2Str(certFile, "UTF8"));
        if (map.get("expiry")!=null) {
        	Date dt = (Date) map.get("expiry");
        	System.out.println("证书有效期:"+DateTimeTool.dateToDateString(dt));
        }
        System.out.println(JsonTool.beanToJson(map));
    }
}


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值