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