数字签名算法可以看做是一种带有密钥(公钥+私钥)的消息摘要算法,也就是说,数据签名算法是非对称加密算法和消息摘要算法的结合体。该算法包含签名和验证两项操作,遵循 “私钥签名,公钥验证” 的签名/验证方式。
数字签名算法消息传递模型
1、甲方构建密钥对,并能公布公钥给乙方。
2、甲方想乙方发送数据需要附加签名。
3、乙方使用公钥和签名验证数据。
![](https://img.haomeiwen.com/i13236281/8b5f8633a140c293.png)
经典数字签名算法——RSA
RSA数字签名算法主要可以分为:MD、SHA两类。该算法公钥通常要比私钥短。
![](https://img.haomeiwen.com/i13236281/7e57d7dc631cf0a9.png)
RSA数字签名示例:
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Before;
import org.junit.Test;
public class RSACoder {
/**
* 数字签名
* 密钥算法
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 数字签名
* 签名/验证算法
*/
public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA密钥长度 默认1024位,
* 密钥长度必须是64的倍数,
* 范围在512至65536位之间。
*/
private static final int KEY_SIZE = 512;
/**
* 签名
*
* @param data
* 待签名数据
* @param privateKey
* 私钥
* @return byte[] 数字签名
* @throws Exception
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
// 转换私钥材料
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initSign(priKey);
// 更新
signature.update(data);
// 签名
return signature.sign();
}
/**
* 校验
*
* @param data
* 待校验数据
* @param publicKey
* 公钥
* @param sign
* 数字签名
*
* @return boolean 校验成功返回true 失败返回false
* @throws Exception
*
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign)
throws Exception {
// 转换公钥材料
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成公钥
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initVerify(pubKey);
// 更新
signature.update(data);
// 验证
return signature.verify(sign);
}
/**
* 取得私钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 初始化密钥
*
* @return Map 密钥对儿 Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 实例化密钥对儿生成器
KeyPairGenerator keyPairGen = KeyPairGenerator
.getInstance(KEY_ALGORITHM);
// 初始化密钥对儿生成器
keyPairGen.initialize(KEY_SIZE);
// 生成密钥对儿
KeyPair keyPair = keyPairGen.generateKeyPair();
// 公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 封装密钥
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 公钥
*/
private byte[] publicKey;
/**
* 私钥
*/
private byte[] privateKey;
/**
* 初始化密钥
*
* @throws Exception
*/
@Before
public void initKey() throws Exception {
Map<String, Object> keyMap = RSACoder.initKey();
publicKey = RSACoder.getPublicKey(keyMap);
privateKey = RSACoder.getPrivateKey(keyMap);
System.err.println("公钥: \n" + Base64.encodeBase64String(publicKey));
System.err.println("私钥: \n" + Base64.encodeBase64String(privateKey));
}
/**
* 校验
*
* @throws Exception
*/
@Test
public void testSign() throws Exception {
String inputStr = "RSA数字签名";
byte[] data = inputStr.getBytes();
// 产生签名
byte[] sign = RSACoder.sign(data, privateKey);
System.err.println("签名:\n" + Hex.encodeHexString(sign));
// 验证签名
boolean status = RSACoder.verify(data, publicKey, sign);
System.err.println("状态:\n" + status);
assertTrue(status);
}
}
数字签名标准算法——DSA
在实现层面上,可以认为DSA算法实现就是RSA算法实现的精简版。DSA算法仅支持SHA系列消息摘要算法。
![](https://img.haomeiwen.com/i13236281/f8841eaa9961abdf.png)
DSA数字签名示例:
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Before;
import org.junit.Test;
public class DSACoder {
/**
* 数字签名密钥算法
*/
public static final String ALGORITHM = "DSA";
/**
* 数字签名
* 签名/验证算法
*/
public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "DSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "DSAPrivateKey";
/**
* DSA密钥长度
* 默认1024位,
* 密钥长度必须是64的倍数,
* 范围在512至1024位之间(含)
*/
private static final int KEY_SIZE = 1024;
/**
* 签名
*
* @param data
* 待签名数据
* @param privateKey
* 私钥
* @return byte[] 数字签名
* @throws Exception
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
// 还原私钥
// 转换私钥材料
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 生成私钥对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initSign(priKey);
// 更新
signature.update(data);
// 签名
return signature.sign();
}
/**
* 校验
*
* @param data
* 待校验数据
* @param publicKey
* 公钥
* @param sign
* 数字签名
*
* @return boolean 校验成功返回true 失败返回false
* @throws Exception
*
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign)
throws Exception {
// 还原公钥
// 转换公钥材料
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 取公钥匙对象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 实例话Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initVerify(pubKey);
// 更新
signature.update(data);
// 验证
return signature.verify(sign);
}
/**
* 生成密钥
*
* @return 密钥对象
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 初始化密钥对儿生成器
KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
// 实例化密钥对儿生成器
keygen.initialize(KEY_SIZE, new SecureRandom());
// 实例化密钥对儿
KeyPair keys = keygen.genKeyPair();
DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();
// 封装密钥
Map<String, Object> map = new HashMap<String, Object>(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 公钥
*/
private byte[] publicKey;
/**
* 私钥
*/
private byte[] privateKey;
/**
* 初始化密钥
*
* @throws Exception
*/
@Before
public void initKey() throws Exception {
Map<String, Object> keyMap = DSACoder.initKey();
publicKey = DSACoder.getPublicKey(keyMap);
privateKey = DSACoder.getPrivateKey(keyMap);
System.err.println("公钥: \n" + Base64.encodeBase64String(publicKey));
System.err.println("私钥: \n" + Base64.encodeBase64String(privateKey));
}
/**
* 校验签名
*
* @throws Exception
*/
@Test
public void test() throws Exception {
String inputStr = "DSA数字签名";
byte[] data = inputStr.getBytes();
// 产生签名
byte[] sign = DSACoder.sign(data, privateKey);
System.err.println("签名:\r" + Hex.encodeHexString(sign));
// 验证签名
boolean status = DSACoder.verify(data, publicKey, sign);
System.err.println("状态:\r" + status);
assertTrue(status);
}
}
椭圆曲线数字签名算法——ECDSA
ECDSA算法相对于传统签名算法具有速度快、强度高、签名短等优点。微软操作系统及办公软件的序列号验证就使用了该算法。
算法 | 密钥长度 | 默认长度 | 签名长度 | 实现的方 |
---|---|---|---|---|
NONEwithECDSA | 112-571 | 256 | 128 | JDK/BC |
RIPEMD160withECDSA | 同上 | 256 | 160 | BC |
SHA1withECDSA | ... | 256 | 160 | JDK/BC |
SHA224withECDSA | ... | 256 | 224 | BC |
SHA256withECDSA | ... | 256 | 256 | JDK/BC |
SHA384withECDSA | ... | 256 | 384 | JDK/BC |
SHA512withECDSA | ... | 256 | 512 | JDK/BC |
ECDSA算法示例:
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Before;
import org.junit.Test;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class ECDSACoder {
/**
* 数字签名 密钥算法
*/
private static final String KEY_ALGORITHM = "ECDSA";
/**
* 数字签名 签名/验证算法
*
* Bouncy Castle支持以下7种算法
* NONEwithECDSA
* RIPEMD160withECDSA
* SHA1withECDSA
* SHA224withECDSA
* SHA256withECDSA
* SHA384withECDSA
* SHA512withECDSA
*/
private static final String SIGNATURE_ALGORITHM = "SHA512withECDSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "ECDSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "ECDSAPrivateKey";
/**
* 初始化密钥
*
* @return Map 密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
BigInteger p = new BigInteger(
"883423532389192164791648750360308885314476597252960362792450860609699839");
ECFieldFp ecFieldFp = new ECFieldFp(p);
BigInteger a = new BigInteger(
"7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc",
16);
BigInteger b = new BigInteger(
"6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a",
16);
EllipticCurve ellipticCurve = new EllipticCurve(ecFieldFp, a, b);
BigInteger x = new BigInteger(
"110282003749548856476348533541186204577905061504881242240149511594420911");
BigInteger y = new BigInteger(
"869078407435509378747351873793058868500210384946040694651368759217025454");
ECPoint g = new ECPoint(x, y);
BigInteger n = new BigInteger(
"883423532389192164791648750360308884807550341691627752275345424702807307");
ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g,
n, 1);
// 实例化密钥对儿生成器
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对儿生成器
kpg.initialize(ecParameterSpec, new SecureRandom());
// 生成密钥对儿
KeyPair keypair = kpg.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keypair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keypair.getPrivate();
// 封装密钥
Map<String, Object> map = new HashMap<String, Object>(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 签名
*
* @param data
* 待签名数据
* @param privateKey
* 私钥
* @return byte[] 数字签名
* @throws Exception
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 转换私钥材料
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initSign(priKey);
// 更新
signature.update(data);
// 签名
return signature.sign();
}
/**
* 校验
*
* @param data
* 待校验数据
* @param publicKey
* 公钥
* @param sign
* 数字签名
* @return boolean 校验成功返回true 失败返回false
* @throws Exception
*
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign)
throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 转换公钥材料
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成公钥
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initVerify(pubKey);
// 更新
signature.update(data);
// 验证
return signature.verify(sign);
}
/**
* 公钥
*/
private byte[] publicKey;
/**
* 私钥
*/
private byte[] privateKey;
/**
* 初始化密钥
*
* @throws Exception
*/
@Before
public void initKey() throws Exception {
Map<String, Object> keyMap = ECDSACoder.initKey();
publicKey = ECDSACoder.getPublicKey(keyMap);
privateKey = ECDSACoder.getPrivateKey(keyMap);
System.err.println("公钥: \n" + Base64.encodeBase64String(publicKey));
System.err.println("私钥: \n" + Base64.encodeBase64String(privateKey));
}
/**
* 校验
*
* @throws Exception
*/
@Test
public void test() throws Exception {
String inputStr = "ECDSA 数字签名";
byte[] data = inputStr.getBytes();
// 产生签名
byte[] sign = ECDSACoder.sign(data, privateKey);
System.err.println("签名:\r" + Hex.encodeHexString(sign));
// 验证签名
boolean status = ECDSACoder.verify(data, publicKey, sign);
System.err.println("状态:\r" + status);
assertTrue(status);
}
}
网友评论