RSA签名和验签Util

1502888-20190427165710247-646166787.jpg

1、DigitalSign类

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 证书加解密/签名/验签类
 */
public class DigitalSign {
    
    private static DigitalSign single=null;

    /** 编码 格式*/
    private static String encoding = "UTF-8";
    /** 算法 */
    private String arithmetic = "SHA1withRSA";

    /**私钥缓存**/
    private ConcurrentHashMap<String, RSAPrivateCrtKey> map = new ConcurrentHashMap<String, RSAPrivateCrtKey>();

    private DigitalSign(String encoding){
    }

    private DigitalSign(){

    }
    
    /**
     * 初始化 实例
     */
    public static synchronized void init() {  
        if (single == null) {    
            single = new DigitalSign(encoding); 
        }    
    }

    /**
     * 初始化 实例
     */
    public static synchronized void init(String charset) {
        if (single == null) {
            single = new DigitalSign();
            encoding = charset;
        }
    }

    //静态工厂方法   
    public static DigitalSign getInstance() {
        if (single == null) {    
            init(); //为空的时候同步实例
        }    
        return single;  
    }

    //静态工厂方法
    public static DigitalSign getInstance(String charset) {
        if (single == null) {
            init(charset); //为空的时候同步实例
        }
        return single;
    }
    
    /**
     * 私钥签名  
     * @param tobeSigned
     * @param priKeyPath classpath 下路径
     * @param password
     * @return
     * @throws CryptException
     */
    public String SignMsgByRelativePath(final String tobeSigned, final String priKeyPath, final String password) throws CryptException {
        RSAPrivateCrtKey priKey = getPriKeyByRelativePath(priKeyPath, password);
        return sign(priKey, tobeSigned);
    }
    
    
    /**
     * 私钥签名
     * @param tobeSigned
     * @param priKeyStream 私钥文件流
     * @param password
     * @return
     * @throws CryptException
     */
    public String SignMsgByInputStream(String tobeSigned,InputStream priKeyStream,String password) throws CryptException{
        RSAPrivateCrtKey priKey = getPriKeyByInputStream(priKeyStream, password);
        return sign(priKey, tobeSigned);
    }
    
    /**
     * 私钥签名
     * @param tobeSigned
     * @param priKeyPath 私钥绝对路径
     * @param password
     * @return
     * @throws CryptException
     */
    public String signMsgByAbsolutePath(String tobeSigned,String priKeyPath,String password) throws CryptException{
        RSAPrivateCrtKey priKey = map.get(priKeyPath);
        if(priKey == null){
            priKey = getPriKeyByAbsolutePath(priKeyPath, password);
        }
        return sign(priKey, tobeSigned);
    }


    /**
     * 私钥签名
     * @param tobeSigned
     * @param priKeyPath 私钥绝对路径
     * @param password
     * @return
     * @throws CryptException
     */
    public String signMsgByAbsolutePath(Map<String, Object> tobeSigned,String priKeyPath,String password) throws CryptException{
        RSAPrivateCrtKey priKey = map.get(priKeyPath);
        if(priKey == null){
            priKey = getPriKeyByAbsolutePath(priKeyPath, password);
        }
        return sign(priKey, tobeSigned);
    }
    
    /**
     * 公钥验签 
     * @param tobeVerfied
     * @param plainText
     * @param pubKey 文件输入流
     * @return
     * @throws CryptException
     */
    public boolean verifyMsgByInputSteam(String tobeVerfied,String plainText,InputStream pubKey) throws CryptException{
        //获取公钥
        RSAPublicKey pubkey = getPubKeyByInputSteam(pubKey);
        return verify(pubkey, tobeVerfied, plainText);
    }
    
    /**
     * 通过公钥验签
     * @param tobeVerified
     * @param plainText
     * @param pubkeyPath 公钥绝对路径
     * @return
     * @throws CryptException
     */
    public boolean verifyMsgByAbsolutePath(String tobeVerified,String plainText,String pubkeyPath) throws CryptException{
        RSAPublicKey pubkey = getPubKeyByAbsolutePath(pubkeyPath);
        return verify(pubkey, tobeVerified, plainText);
    }


    /**
     * 通过公钥验签
     * @param tobeVerified
     * @param map
     * @param pubkeyPath 公钥绝对路径
     * @return
     * @throws CryptException
     */
    public boolean verifyMsgByAbsolutePath(String tobeVerified,Map<String, Object> map,String pubkeyPath) throws CryptException{
        RSAPublicKey pubkey = getPubKeyByAbsolutePath(pubkeyPath);
        return verify(pubkey, tobeVerified, map);
    }
    
    /**
     * 公钥验签
     * @param tobeVerfied
     * @param plainText
     * @param CertFile 文件classpath下路径
     * @return
     * @throws CryptException
     */
    public boolean verifyMsgByRelativePath(String tobeVerfied, String plainText, String CertFile) throws CryptException {
        RSAPublicKey pubkey = getPubKeyByRelativePath(CertFile);
        return verify(pubkey, tobeVerfied, plainText);
    }

    /**
     * 使用公钥对数据加密
     * @param TobeEncryptMsg 待加密的明文字符串
     * @param certFile 公钥路径[相对地址 classpath下]
     * @return 加密后的字符串
     * @throws CryptException 错误信息
     */
    public String encryptMsg(String TobeEncryptMsg, String certFile) throws CryptException {
        //获取公钥
        RSAPublicKey pubKey = getPubKeyByRelativePath(certFile);
        Cipher instance;
        try {
            instance = Cipher.getInstance(pubKey.getAlgorithm());
            instance.init(Cipher.ENCRYPT_MODE, pubKey);
            String encryMsg = Base64.encodeBase64String(instance.doFinal(Base64.encodeBase64(TobeEncryptMsg.getBytes(encoding))));
            return encryMsg;
        } catch (Exception e) {
            throw new CryptException("加密失败",e);
        }
    }   
    
    /**
     * 私钥解密
     * @param TobeDecodeMsg 待解密的加密字符串
     * @param priFile 私钥路径[相对路径 classpath下]
     * @param passWord 私钥密码
     * @return 解密后的明文字符串
     * @throws CryptException 错误信息
     */
    public String decodeMsg(String TobeDecodeMsg, String priFile,
            String passWord) throws CryptException{
        RSAPrivateCrtKey priKey = getPriKeyByRelativePath(priFile, passWord);
        Cipher instance;
        try {
            instance = Cipher.getInstance(priKey.getAlgorithm());
            instance.init(Cipher.DECRYPT_MODE, priKey);
            String string = new String(Base64.decodeBase64(instance.doFinal(Base64.decodeBase64(TobeDecodeMsg))), encoding);
            return string;
        } catch (Exception e) {
            throw new CryptException("解密失败",e);
        }
    }

    /**
     * 获取私钥 文件流
     * @param keyfileStream
     * @param password
     * @return
     * @throws CryptException
     */
    private RSAPrivateCrtKey getPriKeyByInputStream(InputStream keyfileStream,String password) throws CryptException{
        return getPriKey(keyfileStream, password);      
    }
    
    /**
     * 通过文件绝对路径加载私钥
     * @param priKeyPath 绝对路径
     * @param password
     * @return
     * @throws CryptException
     */
    private RSAPrivateCrtKey getPriKeyByAbsolutePath(String priKeyPath,String password) throws CryptException{
        FileInputStream file;
        try {
            file = new FileInputStream(priKeyPath);
        } catch (FileNotFoundException e) {
            throw new CryptException("秘钥路径不正确",e);
        }
        RSAPrivateCrtKey priKey = getPriKey(file, password);
        map.put(priKeyPath, priKey);
        return priKey;
    }
    
    /**
     * 获取私钥 [classpath]
     * @param KeyFile
     * @param passWord
     * @return
     */
    private RSAPrivateCrtKey getPriKeyByRelativePath(String KeyFile,String passWord) throws CryptException{
        //获取项目 相对路径
        ClassLoader cl = DigitalSign.class.getClassLoader();
        InputStream fiKeyFile = cl.getResourceAsStream(KeyFile);
        return getPriKey(fiKeyFile, passWord);
    }
    
    /**
     * 获取公钥 [文件输入流]
     * @param pubKey
     * @return
     * @throws CryptException
     */
    private RSAPublicKey getPubKeyByInputSteam(InputStream pubKey) throws CryptException{
        return getPublicKey(pubKey);
    }

    
    /**
     * 获取公钥 [classpath下]
     * @param CertFile
     * @return
     */
    private RSAPublicKey getPubKeyByRelativePath(String CertFile) throws CryptException{
        //读取公钥文件
        ClassLoader cl = DigitalSign.class.getClassLoader();
        InputStream certfile = cl.getResourceAsStream(CertFile);
        //获取公钥
        return getPublicKey(certfile);
    }
    
    /**
     * 获取公钥 通过绝对路径
     * @param pubKeyPath
     * @return
     * @throws CryptException
     */
    private RSAPublicKey getPubKeyByAbsolutePath(String pubKeyPath) throws CryptException{
        try{
            FileInputStream pubKey = new FileInputStream(pubKeyPath);
            return getPublicKey(pubKey);
        }catch(Exception e){
            throw new CryptException("文件读取失败",e);
        }
    }
    
    /**
     * 获取公钥
     * @param pubKey 公钥流
     * @return
     * @throws CryptException
     */
    private RSAPublicKey getPublicKey(InputStream pubKey) throws CryptException{
        X509Certificate x509cert = null;
        try {
            //实例化 x509
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            x509cert = (X509Certificate) cf.generateCertificate(pubKey);
        } catch (CertificateException e) {
            if (pubKey != null){
                try {
                    pubKey.close();
                } catch (IOException e1) {
                    throw new CryptException("文件流关闭异常",e1);
                }
            }           
            throw new CryptException("初始化公钥异常",e);
        }
        //读取公钥
        RSAPublicKey pubkey = (RSAPublicKey) x509cert.getPublicKey();       
        return pubkey;
    }
    
    /**
     * 获取私钥
     * @param priKey
     * @param keyPassword
     * @return
     * @throws CryptException
     */
    private RSAPrivateCrtKey getPriKey(InputStream priKey,String keyPassword) throws CryptException{
        String keyAlias = null;
        RSAPrivateCrtKey prikey = null;     
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(priKey, keyPassword.toCharArray());
            
            Enumeration<?> myEnum = ks.aliases();
            while (myEnum.hasMoreElements()) {
                keyAlias = (String) myEnum.nextElement();
                if (ks.isKeyEntry(keyAlias)) {
                    prikey = (RSAPrivateCrtKey) ks.getKey(keyAlias, keyPassword.toCharArray());
                    break;
                }
            }           
        } catch (Exception e) {
            if (priKey != null){
                try {
                    priKey.close();
                } catch (IOException e1) {
                    throw new CryptException("流关闭异常",e1);
                }
            }
            throw new CryptException("加载私钥失败",e);
        }
        
        if(prikey == null){
            throw new CryptException("私钥不存在");
        }
        
        return prikey;      
    }
    
    /**
     * 签名
     * @param priKey 私钥
     * @param tobeSigned 待签字符串
     * @return 签名结果
     * @throws CryptException
     */
    private String sign(RSAPrivateCrtKey priKey,String tobeSigned) throws CryptException{
        try {
            Signature sign = Signature.getInstance(arithmetic);
            sign.initSign(priKey);  
            sign.update(tobeSigned.getBytes(encoding)); 
            byte signed[] = sign.sign();
//          byte sign_asc[] = new byte[signed.length * 2];  
//          Hex2Ascii(signed.length, signed, sign_asc);
            
//          return new String(signed,encoding); 
            return Base64.encodeBase64String(signed).replaceAll("\\+", "%2B");
        } catch (Exception e) {
            throw new CryptException("签名失败",e);
        }       
    }

    /**
     * 签名
     * @param priKey 私钥
     * @param map 待签字符串
     * @return 签名结果
     * @throws CryptException
     */
    private String sign(RSAPrivateCrtKey priKey,Map<String, Object> map) throws CryptException{
        try {
            String tobeSigned = appendMap(map);
            Signature sign = Signature.getInstance(arithmetic);
            sign.initSign(priKey);
            sign.update(tobeSigned.getBytes(encoding));
            byte signed[] = sign.sign();
//          byte sign_asc[] = new byte[signed.length * 2];
//          Hex2Ascii(signed.length, signed, sign_asc);

//          return new String(signed,encoding);
            return Base64.encodeBase64String(signed).replaceAll("\\+", "%2B");
        } catch (Exception e) {
            throw new CryptException("签名失败",e);
        }
    }


    /**
     * 连接参数
     * @param map
     * @return
     */
    private static String appendMap(Map<String, Object> map){
        Set<String> keySet = map.keySet();
        StringBuilder sb = new StringBuilder();
        for (String mapKey : keySet){
            String value = (String)map.get(mapKey);
            sb.append(mapKey).append("=").append(value).append("&");
        }
        return  sb.toString();
    }
    
    /**
     * 验证签名
     * @param pubkey 公钥
     * @param tobeVerfied 已签名字符串
     * @param plainText 待校验字符串
     * @return true || false
     * @throws CryptException
     */
    private boolean verify(RSAPublicKey pubkey,String tobeVerfied,String plainText) throws CryptException{
        try {
            Signature verify = Signature.getInstance(arithmetic);
            verify.initVerify(pubkey);
//          byte signeddata[] = new byte[tobeVerfied.length() / 2];
//          Ascii2Hex(tobeVerfied.length(), tobeVerfied.getBytes(encoding), signeddata);    
            verify.update(plainText.getBytes(encoding));    
            
            return verify.verify(Base64.decodeBase64(tobeVerfied.replaceAll("%2B", "\\+")));
        } catch (Exception e) {
            throw new CryptException("验签失败",e);
        }       
    }


    /**
     * 验证签名
     * @param pubkey 公钥
     * @param tobeVerfied 已经签名的map
     * @param map 待校验的map
     * @return true || false
     * @throws CryptException
     */
    private boolean verify(RSAPublicKey pubkey,String tobeVerfied,Map<String, Object> map) throws CryptException{
        try {
            String plainText =  appendMap(map);
            Signature verify = Signature.getInstance(arithmetic);
            verify.initVerify(pubkey);
//          byte signeddata[] = new byte[tobeVerfied.length() / 2];
//          Ascii2Hex(tobeVerfied.length(), tobeVerfied.getBytes(encoding), signeddata);
            verify.update(plainText.getBytes(encoding));

            return verify.verify(Base64.decodeBase64(tobeVerfied.replaceAll("%2B", "\\+")));
        } catch (Exception e) {
            throw new CryptException("验签失败",e);
        }
    }
}


2、CryptException异常类


/**
 * 密钥异常类
 */
public class CryptException extends Exception{

    private static final long serialVersionUID = 2843021655596315128L;

    public CryptException(String message){
        super(message);
    }

    public CryptException(String message, Throwable cause) {
        super(message, cause);
    }

    public CryptException(Throwable cause) {
        super(cause);
    }
}

3、加签示例

Map<String, String> map = new TreeMap<String, String>();
//....省略map put值...
String sign = DigitalSign.getInstance().signMsgByAbsolutePath(map, path, 
password);
map.put("sign", sign);

4、验签示例

map.remove("sign");
DigitalSign digitalSign = DigitalSign.getInstance();
boolean result = digitalSign.verifyMsgByAbsolutePath(sign, params, merchCertPath);

转载于:https://www.cnblogs.com/ranandrun/p/DigitalSign.html

<think>嗯,用户想了解如何在Java应用中实现RSA。首先,我需要回忆一下RSA签名的基本流程。根据引用[2]里的代码示例,签名的时候用的是私钥,而应该用公钥对吧?那的步骤应该是初始化Signature对象为模式,传入公钥,然后更新数据签名。 然后,用户可能需要的代码结构。引用[2]展示了签名的代码,那么应该类似,但使用不同的方法,比如initVerifyverify。另外,引用[3][4]提到了公钥的使用加密解密的过程,这可能对理解如何加载公钥有帮助。 需要注意的几个点:公钥的获取方式,比如从文件或者字符串加载。如何处理密钥的格式,比如PKCS#8还是其他。还有数据的一致性,比如签名的数据是否相同,编码是否一致,比如Base64的处理。 可能的问题还有异常处理,比如InvalidKeyException或SignatureException,这些应该在代码中捕获处理。此外,示例代码可能需要使用Java标准库中的类,比如KeyFactory、X509EncodedKeySpec等来生成公钥对象。 还要确保用户了解签名的区别,引用[2]中提到签名的初始化是initSign,而是initVerify,这一点很重要。同时,引用[4]中的解密部分可能混淆,需要区分清楚。 最后,根据用户的引用,可能需要补充关于前后台交互的场景,比如引用[3]提到的前台用jsrsasign库,后台用Java实现,但用户的问题集中在Java应用内的实现,所以可能需要专注于Java部分的代码。 总结步骤:加载公钥,初始化的Signature对象,更新数据,签名。给出具体的代码示例,并说明可能的异常注意事项。</think>### Java应用中实现RSA的步骤 #### 1. **核心流程说明** RSA用于证数据的完整性来源真实性,需使用公钥证私钥生成的签名。核心步骤包括: - **加载公钥**:从文件、证书或字符串中获取公钥 - **初始化对象**:指定算法(如`SHA256withRSA`) - **更新原始数据**:需与签名时的数据完全一致 - **执行**:比对签名结果 #### 2. **代码实现示例** ```java import java.security.*; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class RSAVerifyExample { public static boolean verifySignature(String data, String publicKeyStr, String signatureStr) { try { // 1. 加载公钥 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(keySpec); // 2. 初始化 Signature sig = Signature.getInstance("SHA256withRSA"); sig.initVerify(publicKey); // 3. 更新原始数据 sig.update(data.getBytes()); // 4. 执行 byte[] signature = Base64.getDecoder().decode(signatureStr); return sig.verify(signature); } catch (Exception e) { e.printStackTrace(); return false; } } } ``` #### 3. **关键注意点** 1. **密钥格式匹配**: - 公钥需符合X.509编码规范(`X509EncodedKeySpec`) - 若使用OpenSSL生成的密钥需转换格式:`openssl rsa -pubin -in public.pem -RSAPublicKey_out`[^3] 2. **数据一致性**: - 时输入的`data`必须与签名时的原始数据完全一致(包括编码格式) 3. **异常处理**: ```java catch (InvalidKeyException e) { // 密钥格式错误或类型不匹配 } catch (SignatureException e) { // 签名数据被篡改 } ``` #### 4. **典型应用场景** - API接口安全证(如支付回调) - 软件更新包的完整性校 - 电子合同签名证(引用[3]中提到的前后端交互场景)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值