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