Java中使用PSS填充的签名验签工具

引入依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.62</version>
</dependency>

签名验签工具类

import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.PSSParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder;

/**
 * Description
 * <p>
 *
 * @author x00482439
 * @version v1.0, 2019-06-10
 * @since Security SDK
 */
public class RSASignature {

    private static final Logger Logger = LoggerFactory.getLogger(RSASignature.class);

    /**
     * 签名算法
     */
    private static final String SIGN_ALGORITHMS = "SHA256withRSA";
    private static final String SIGN_ALGORITHMS_PSS = "SHA256withRSA/PSS";
    private static final String ENCODE_ALGORITHM = "SHA-256";

    /**
     * 生成文件摘要
     *
     * @param content 文件内容
     * @param encode 字符集编码
     * @return 文件摘要
     */
    public static String digest(String content, String encode) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
            messageDigest.update(content.getBytes(encode));
            byte[] digestBytes = messageDigest.digest();
            return new String(Base64.getEncoder().encode(digestBytes), ICommon.GBK_ENCODEING);
        } catch (Exception e) {
            Logger.error("Summary calculation failed. {}", e.getMessage());
        }
        return null;
    }

    /**
     * RSA签名,结果写入签名值文件中
     *
     * @param file 待签名文件
     * @param signFile 签名值文件
     * @param signKeyFile 签名私钥文件
     * @param keyPassword 签名私钥口令
     */
    public static void sign(File file, String signFile, String signKeyFile, String keyPassword) {
        try {
            // 先解密私钥
            String encrypted = new String(Files.readAllBytes(Paths.get(signKeyFile)));
            encrypted = encrypted.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
            encrypted = encrypted.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
            encrypted = encrypted.replaceAll("\\n", "");
            EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo(Base64.getDecoder().decode(encrypted));
            PBEKeySpec keySpec = new PBEKeySpec(keyPassword.toCharArray()); // password
            SecretKeyFactory pbeKeyFactory = SecretKeyFactory.getInstance(pkInfo.getAlgName());
            PKCS8EncodedKeySpec encodedKeySpec = pkInfo.getKeySpec(pbeKeyFactory.generateSecret(keySpec));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyFactory.generatePrivate(encodedKeySpec);

            // 开始签名
            Security.addProvider(new BouncyCastleProvider());
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS_PSS);
            signature.setParameter(new PSSParameterSpec(MGF1ParameterSpec.SHA256.getDigestAlgorithm(), "MGF1",
                MGF1ParameterSpec.SHA256, 32, 1));
            signature.initSign(priKey);
            signature.update(Files.readAllBytes(file.toPath()));
            byte[] signed = signature.sign();
            String signStr = new String(Base64.getEncoder().encode(signed), StandardCharsets.UTF_8);
            System.out.println(signStr);
            Files.write(Paths.get(signFile), signStr.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            Logger.error("digital signature failed", e);
        }
    }

    /**
     * RSA验签名检查
     *
     * @param originFile 待验证文件
     * @param signFile 签名值文件
     * @param signCertFile 签名证书文件
     * @return 是否验签成功
     */
    public static boolean verify(File originFile, String signFile, String signCertFile) {
        try {
            CertificateFactory fact = CertificateFactory.getInstance("X.509");
            X509Certificate certificate = (X509Certificate) fact.generateCertificate(new FileInputStream(signCertFile));
            PublicKey pk = certificate.getPublicKey();
            byte[] keyBytes = pk.getEncoded();
            String publicKey = new BASE64Encoder().encode(keyBytes);
            publicKey = publicKey.replaceAll("\\r?\\n", "");
            byte[] encodedKey = Base64.getDecoder().decode(publicKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            Security.addProvider(new BouncyCastleProvider());
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS_PSS);
            signature.setParameter(new PSSParameterSpec(MGF1ParameterSpec.SHA256.getDigestAlgorithm(), "MGF1",
                MGF1ParameterSpec.SHA256, 32, 1));
            signature.initVerify(pubKey);
            signature.update(Files.readAllBytes(originFile.toPath()));
            String signStr = new String(Files.readAllBytes(Paths.get(signFile)), StandardCharsets.UTF_8);
            return signature.verify(Base64.getDecoder().decode(signStr.replaceAll("\\n", "")));
        } catch (Exception e) {
            Logger.error("Digital signature verification failed. {}", e.getMessage());
        }
        return false;
    }
}

备注

如果是openssl生成的私钥,则默认为PKCS1,需要转换

PKCS1私钥转换为PKCS8

openssl pkcs8 -topk8 -inform PEM -in sign.key -outform pem -out sign2.key
mv sign2.key sign.key

转载于:https://my.oschina.net/yidao620c/blog/3096306

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值