1、定义与核心功能
- 定义:通过密码学方法生成的字符串,用于验证数据来源、完整性及不可否认性。
- 核心功能:
- 身份验证:确认信息由签名者发送。
- 数据完整性:确保信息未被篡改。
- 不可否认性:签名者无法否认签名行为。
- 不可伪造性:他人无法伪造签名。
2、实现方式
- 分类:
- 映象式签名:直接对明文加密(如RSA原始签名)。
- 印记式签名:对明文哈希后加密(如RSA+SHA-256),速度更快,更实用。
- 关键技术:
- 哈希函数:将任意长度数据转换为固定长度摘要(如SHA-256)。
- 非对称加密:私钥签名,公钥验证(如RSA、ECDSA)。
- 对称加密签名:如Lamport签名(知识库提到的对称算法)。
3、常用签名算法
(1)、RSA签名算法(推荐)
- 步骤:
- 哈希计算:对明文 M 计算哈希值 H = Hash(M)(如SHA-256)。
- 私钥签名:用私钥 d 加密哈希值,生成签名 S = H^d mod n。
- 公钥验证:接收方用公钥 e 解密签名,得到 H’ = S^e mod n,并与重新计算的哈希值对比。
- 特点:
- 安全性:依赖RSA的因数分解难题。
- 速度慢:签名和验证速度慢,适合小数据。
- 兼容性:广泛支持(如SSL证书、代码签名)。
- 应用场景:
- 证书签名:X.509证书(如SSL/TLS)。
- 代码签名:Windows、macOS软件签名。
(2)、DSA(数字签名算法)
- 基于离散对数问题:
- 密钥生成:
- 公共参数:素数 p、子群阶 q、生成元 g。
- 私钥 x(随机数),公钥 y = g^x mod p。
- 签名生成:
- 随机数 k,计算 r = (g^k mod p) mod q。
- s = (k^{-1}(H(M) + x·r)) mod q。
- 签名对为 (r, s)。
- 验证:
- 计算 w = s^{-1} mod q。
- u1 = H(M)·w mod q,u2 = r·w mod q。
- v = (g{u1}·y{u2} mod p) mod q。
- 若 v = r,则签名有效。
- 密钥生成:
- 特点:
- 标准化:NIST标准,专为签名设计。
- 密钥管理:随机数 k 泄露会导致私钥泄露。
- 效率:比RSA稍快,但密钥长度较长(如3072位)。
- 应用场景:
- 政府系统:美国联邦机构。
- SSL证书:部分CA使用。
(3)、ECDSA(推荐)(椭圆曲线数字签名算法)
- 基于椭圆曲线离散对数问题:
- 密钥生成:
- 公共参数:椭圆曲线 E、基点 G。
- 私钥 d(随机数),公钥 Q = d·G。
- 签名生成:
- 随机数 k,计算点 (x, y) = k·G,r = x mod n。
- s = (k^{-1}(H(M) + d·r)) mod n。
- 签名对为 (r, s)。
- 验证:
- w = s^{-1} mod n。
- u1 = H(M)·w mod n,u2 = r·w mod n。
- 点 (x, y) = u1·G + u2·Q。
- 若 r ≡ x mod n,则有效。
- 密钥生成:
- 特点:
- 高效性:256位ECDSA ≈ 3072位RSA的安全性。
- 短密钥:节省带宽(如比特币使用256位)。
- 应用场景:
- 区块链:比特币、以太坊交易签名。
- 移动设备:资源受限场景。
(4)、Lamport签名(对称签名)
- 基于对称加密(知识库提到的Lamport-Diffie算法):
- 密钥生成:
- 随机生成 2n 个密钥 B_i(n为报文长度),加密得到 C_i = Encrypt(A, B_i)。
- 公钥为 C,私钥为 A。
- 签名生成:
- 对报文 M 的每一位 M_i:
- 若 M_i = 0,取 A 的第 i 位。
- 若 M_i = 1,取 A 的第 i+1 位。
- 签名是选取的 n 个密钥位。
- 对报文 M 的每一位 M_i:
- 验证:
- 接收方用公钥 C 验证签名中的密钥位是否匹配。
- 密钥生成:
- 特点:
- 安全性:一次一密,但仅能签一条消息。
- 效率低:密钥和签名长度随报文增长而爆炸式增长。
- 应用场景:
- 理论研究:早期对称签名的探索。
(5)、Schnorr签名
- 基于离散对数问题:
- 密钥生成:
- 参数:素数 p、阶为 q 的子群生成元 g。
- 私钥 x,公钥 y = g^x mod p。
- 签名生成:
- 随机数 k,计算 r = g^k mod p。
- e = Hash(M || r)。
- s = k - x·e mod q。
- 签名对 (e, s)。
- 验证:
- 检查 g^s ≡ r·y^e mod p。
- 密钥生成:
- 特点:
- 短签名:签名长度固定。
- 抗量子潜力:可能适配后量子算法。
- 应用场景:
- 区块链:比特币Taproot升级后广泛采用。
(6)、HMAC(对称加密)(分布式不推荐)
- HMAC通过哈希函数(如MD5、SHA-1、SHA-256)和共享密钥生成固定长度的消息摘要(即签名)。
- 其核心步骤如下:
- 预处理密钥:
- 如果密钥长度超过哈希函数的块大小(如SHA-1的64字节),先用哈希函数处理密钥。
- 否则,用零字节填充到块大小。
- 生成签名:
- 将密钥与ipad(内部填充,0x36重复)异或,得到内层密钥。
- 将消息与内层密钥拼接后哈希,得到中间结果。
- 将密钥与opad(外部填充,0x5C重复)异或,得到外层密钥。
- 将外层密钥与中间结果拼接后再次哈希,得到最终签名。
- 应用场景: API签名、消息认证、身份验证
4、哈希函数在签名中的作用
(1)、哈希函数类型
- 已不安全的算法:
- MD5:已被碰撞攻击破解(如2017年SHA-1碰撞)。
- SHA-1:2017年被Google破解,已淘汰。
(2)、安全算法:
- SHA-2系列(SHA-256、SHA-384等):目前主流,未被破解。
- SHA-3:基于Keccak算法,可替代SHA-2。
(3)、哈希函数的作用
- 压缩数据:将任意长度明文转换为固定长度摘要。
- 抗碰撞:确保不同输入无法生成相同哈希值。
- 单向性:无法从哈希值反推原始数据。
5、数字签名的典型应用场景
(1)、证书签名
- SSL/TLS证书:CA用私钥签名服务器公钥,浏览器验证。
- 代码签名:软件开发者签名确保代码未被篡改。
(2)、区块链
- 比特币交易:ECDSA签名验证交易合法性。
- 智能合约:以太坊使用ECDSA或Ed25519。
(3)、电子邮件
- PGP/GPG:用RSA或DSA签名和加密邮件。
(4)、政府与金融
- 电子政务:电子公文、电子合同签名。
- 金融交易:银行间支付指令签名。
6、签名算法的优缺点对比
算法选择:
- RSA:适合兼容性要求高的场景(如SSL证书),密钥长度建议 2048位以上。
- ECDSA:高效且密钥短(如256位),适合移动端和区块链(如比特币)。
- DSA:NIST标准,但仅用于签名,密钥需符合特定参数。
- DH:用于密钥协商,需结合对称加密(如AES)实现通信加密。
7、注意事项与最佳实践
(1)、避免过时算法:
- 禁用 MD5、SHA-1。
- 推荐 SHA-256 或 SHA-3。
(2)、密钥管理:
- 私钥需存储于安全设备(如HSM)。
- 随机数生成需高熵(如ECDSA的随机数 k)。
(3)、后量子安全:
- NIST正标准化 后量子签名算法(如CRYSTALS-Dilithium)。
(4)、签名与加密结合:
- 常用 混合方案:如TLS中先用ECDH协商密钥,再用AES加密数据,同时用ECDSA签名。
8、代码示例
(1)、RSA算法示例
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
public class RSAExample {
public static void main(String[] args) throws Exception {
// 1. 生成RSA密钥对(2048位,安全强度更高)
KeyPair keyPair = generateRSAKeyPair(2048);
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 2. 要签名的数据
String data = "Hello World!";
byte[] dataBytes = data.getBytes();
// 3. 生成签名(使用SHA256withRSA算法)
byte[] signature = signData(privateKey, dataBytes);
System.out.println("签名结果(Base64): " + Base64.getEncoder().encodeToString(signature));
// 4. 验证签名
boolean isValid = verifySignature(publicKey, dataBytes, signature);
System.out.println("签名验证结果: " + isValid);
}
// 生成RSA密钥对
private static KeyPair generateRSAKeyPair(int keySize) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keySize);
return keyGen.generateKeyPair();
}
// 使用私钥生成签名
private static byte[] signData(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
// 使用公钥验证签名
private static boolean verifySignature(PublicKey publicKey, byte[] data, byte[] signature) throws Exception {
Signature verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(publicKey);
verifier.update(data);
return verifier.verify(signature);
}
}
(2)、ECDSA算法示例
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
public class ECDSAExample {
public static void main(String[] args) throws Exception {
// 1. 生成ECDSA密钥对(使用NIST P-256曲线)
KeyPair keyPair = generateECKeyPair(256);
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 2. 要签名的数据
String data = "Hello World!";
byte[] dataBytes = data.getBytes();
// 3. 生成签名(使用SHA256withECDSA算法)
byte[] signature = signData(privateKey, dataBytes);
System.out.println("签名结果(Base64): " + Base64.getEncoder().encodeToString(signature));
// 4. 验证签名
boolean isValid = verifySignature(publicKey, dataBytes, signature);
System.out.println("签名验证结果: " + isValid);
}
// 生成ECDSA密钥对(椭圆曲线算法)
private static KeyPair generateECKeyPair(int keySize) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(keySize); // 256位对应NIST P-256曲线
return keyGen.generateKeyPair();
}
// 使用私钥生成签名
private static byte[] signData(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
// 使用公钥验证签名
private static boolean verifySignature(PublicKey publicKey, byte[] data, byte[] signature) throws Exception {
Signature verifier = Signature.getInstance("SHA256withECDSA");
verifier.initVerify(publicKey);
verifier.update(data);
return verifier.verify(signature);
}
}
(3)、DSA算法示例
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.DSAParameterSpec;
import java.security.spec.ECParameterSpec;
import java.util.Base64;
public class DSAExample {
public static void main(String[] args) throws Exception {
// 1. 生成DSA密钥对(3072位,符合NIST标准)
KeyPair keyPair = generateDSAKeyPair(3072);
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 2. 要签名的数据
String data = "Hello World!";
byte[] dataBytes = data.getBytes();
// 3. 生成签名(使用SHA256withDSA算法)
byte[] signature = signData(privateKey, dataBytes);
System.out.println("签名结果(Base64): " + Base64.getEncoder().encodeToString(signature));
// 4. 验证签名
boolean isValid = verifySignature(publicKey, dataBytes, signature);
System.out.println("签名验证结果: " + isValid);
}
// 生成DSA密钥对(注意:需指定参数,此处简化使用默认参数)
private static KeyPair generateDSAKeyPair(int keySize) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
// DSA需指定参数(如素数p、q等),此处使用默认参数
keyGen.initialize(keySize); // 3072位符合NIST推荐
return keyGen.generateKeyPair();
}
// 使用私钥生成签名
private static byte[] signData(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA256withDSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
// 使用公钥验证签名
private static boolean verifySignature(PublicKey publicKey, byte[] data, byte[] signature) throws Exception {
Signature verifier = Signature.getInstance("SHA256withDSA");
verifier.initVerify(publicKey);
verifier.update(data);
return verifier.verify(signature);
}
}
(4)、Diffie-Hellman密钥交换示例(仅密钥协商,不直接签名)
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.KeyAgreement;
public class DHExample {
public static void main(String[] args) throws Exception {
// 1. 生成Alice的密钥对
KeyPair aliceKeyPair = generateDHKeyPair(2048);
PrivateKey alicePrivateKey = aliceKeyPair.getPrivate();
PublicKey alicePublicKey = aliceKeyPair.getPublic();
// 2. 生成Bob的密钥对
KeyPair bobKeyPair = generateDHKeyPair(2048);
PrivateKey bobPrivateKey = bobKeyPair.getPrivate();
PublicKey bobPublicKey = bobKeyPair.getPublic();
// 3. Alice计算共享密钥(使用Bob的公钥)
KeyAgreement aliceAgree = KeyAgreement.getInstance("DH");
aliceAgree.init(alicePrivateKey);
aliceAgree.doPhase(bobPublicKey, true);
byte[] aliceSharedKey = aliceAgree.generateSecret();
// 4. Bob计算共享密钥(使用Alice的公钥)
KeyAgreement bobAgree = KeyAgreement.getInstance("DH");
bobAgree.init(bobPrivateKey);
bobAgree.doPhase(alicePublicKey, true);
byte[] bobSharedKey = bobAgree.generateSecret();
// 5. 验证共享密钥是否一致
System.out.println("Alice和Bob的共享密钥是否一致: " + java.util.Arrays.equals(aliceSharedKey, bobSharedKey));
}
// 生成Diffie-Hellman密钥对
private static KeyPair generateDHKeyPair(int keySize) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
keyGen.initialize(keySize);
return keyGen.generateKeyPair();
}
}
(5)、HMAC-SHA256示例
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class HmacExample {
// 生成 HMAC-SHA256 签名
public static String generateHmacSignature(String secretKey, String message) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
mac.init(secretKeySpec);
byte[] hmacBytes = mac.doFinal(message.getBytes()); // 生成签名 字节数组
return Base64.getEncoder().encodeToString(hmacBytes); // 返回 Base64 编码的签名
}
// 验证 HMAC-SHA256 签名
public static boolean verifyHmacSignature(String secretKey, String message, String expectedSignature) throws Exception {
String actualSignature = generateHmacSignature(secretKey, message); // 重新生成一次签名
return actualSignature.equals(expectedSignature); // 对比新签名和传入签名是否一致
}
public static void main(String[] args) {
try {
// 密钥和消息
String secretKey = "my_secret_key";
String message = "this_is_my_message";
// 生成签名
String signature = generateHmacSignature(secretKey, message);
System.out.println("Generated Signature: " + signature);
// 验证签名
boolean isValid = verifyHmacSignature(secretKey, message, signature);
System.out.println("Is Signature Valid? " + isValid);
} catch (Exception e) {
e.printStackTrace();
}
}
}
逆风翻盘,Dare To Be!!!