非对称签名、验签与密钥对生成分享

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 数字签名工具类
 *
 * @author Luyao
 */
public class SignatureKit {

    // 测试
    public static void main(String[] args) {
        // 要签名的内容
        String content = "349382kjfkjdhfgkjfdhg9438759843jkfkjgkfjdnvcb0vb89798";
        // 指定算法
        SignAlgorithm algorithm = SignAlgorithm.EdDSA_ED25519;
        
        // 生成密钥对
        SecretKeyPair keyPair = generateKeyPair(algorithm);
        System.out.println("privateKey:" + keyPair.privateKey());
        System.out.println("publicKey:" + keyPair.publicKey());
        
        // 签名
        String signature = signature(content, keyPair.privateKey(), algorithm);
        System.out.println("signature:" + signature);
        // 验签
        boolean isOk = verify(content, keyPair.publicKey(), algorithm, signature);
        System.out.println(isOk);
    }

    /**
     * 生成密钥对
     *
     * @param algorithm 签名算法
     * @return 密钥对
     */
    public static SecretKeyPair generateKeyPair(SignAlgorithm algorithm) {
        try {
            // 获取指定算法的密钥对生成器
            KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm.getFamilyName());
            // 如果是sha1WithRSA指定生成的密钥长度,否则用默认的长度
            if (algorithm == SignAlgorithm.SHA1_RSA) generator.initialize(1024);
            // 生成密钥对
            KeyPair keyPair = generator.generateKeyPair();
            // 将私钥和公钥通过Base64编码
            Base64.Encoder encoder = Base64.getEncoder();
            String privateKey = encoder.encodeToString(keyPair.getPrivate().getEncoded());
            String publicKey = encoder.encodeToString(keyPair.getPublic().getEncoded());
            return new SecretKeyPair(privateKey, publicKey);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行签名
     *
     * @param content   签名的内容
     * @param key       私钥
     * @param algorithm 签名算法
     * @return 数字签名
     */
    public static String signature(String content, String key, SignAlgorithm algorithm) {
        try {
            Signature signature = Signature.getInstance(algorithm.getAlgorithm());
            KeyFactory keyFactory = KeyFactory.getInstance(algorithm.getFamilyName());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            signature.initSign(privateKey);
            signature.update(content.getBytes());
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 验证签名
     *
     * @param content   签名的内容
     * @param key       公钥
     * @param algorithm 签名算法
     * @param sign      要校验的签名
     * @return 签名是否正确
     */
    public static boolean verify(String content, String key, SignAlgorithm algorithm, String sign) {
        Base64.Decoder decoder = Base64.getDecoder();
        try {
            Signature signature = Signature.getInstance(algorithm.getAlgorithm());
            KeyFactory keyFactory = KeyFactory.getInstance(algorithm.getFamilyName());
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoder.decode(key));
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            signature.initVerify(publicKey);
            signature.update(content.getBytes());
            return signature.verify(decoder.decode(sign));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 签名算法枚举
 *
 * @author Luyao
 */
@Getter
@AllArgsConstructor
public enum SignAlgorithm {

    SHA1_RSA("RSA", "sha1WithRSA"),
    SHA256_RSA("RSA", "sha256WithRSA"),
    SHA256_DSA("DSA", "sha256WithDSA"),
    SHA256_EcDSA("EC", "sha256WithECDSA"),
    EdDSA_ED25519("EdDSA", "Ed25519");

    private final String familyName;
    private final String algorithm;
}
/**
 * 密钥对
 *
 * @author Luyao
 */
public record SecretKeyPair(String privateKey, String publicKey) {
}
非对称加密算法介绍:
常见非对称的加密算法有 DH、RSA、ECC、DSA 等。其中的 RSA 最常用,它的安全性基于“整数分解”的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的。
ECC(Elliptic Curve Cryptography)是非对称加密里的“后起之秀”,它基于“椭圆曲线离散对数”的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。
比起 RSA,ECC 在安全强度和性能上都有明显的优势。160 位的 ECC 相当于 1024 位的 RSA,而 224 位的 ECC 则相当于 2048 位的 RSA。因为密钥短,所以相应的计算量、消耗的内存和带宽也就少,加密解密的性能就上去了,对于现在的移动互联网非常有吸引力。
EdDSA(爱德华曲线算法) 为JDK15新增支持,经测试其密钥和生成的签名长度最短。
Record为JDK14新增的类型,默认带有所有字段的构造函数与各个字段的get方法,并实现了toString、hashCode和equals方法。

更多完整的签名工具已开源:Signature

评论区

caoxusheng

2020-10-25 13:51

SM 国密算法挺不错的

热门分享

扫码入社