- 数字签名
- 1 定义
- 2 数字签名特点
- 3 应用场景
- 4 JDK支持的信息摘要算法
- 5 Bouncy Castle 支持的信息摘要算法
- 6 算法调用示例
数字签名(digital signature)是一种电子签名,也可以表示为一种数学算法,通常用于验证消息(例如,电子邮件、信用卡交易或数字文档)的真实性和完整性。
数字签名并没有创建新的算法,主要是结合使用信息摘要算法(MD,SHA)和非对称加密算法(RSA,DSA)。信息摘要算法用来验证数据完整性,非对称加密算法用来进行身份验证。
消息发送方用摘要算法和私钥加密生成签名,接收方用公钥解密验证签名,再用相同的摘要算法验证数据完整性。
一个典型的消息发送过程如下:
2 数字签名特点- 防篡改:数据不会被修改,MAC算法也有这个特点。
- 防抵赖:消息签署者不能抵赖。
- 防伪造:发送的消息不能够伪造,MAC算法也有这个特点。
数字签名具有许多重要的应用,例如在电子政务活动中的电子公文、网上报税、网上投票,在电子商务活动中的电子订单、电子账单、电子收据、电子合同、电子现金等电子文档都需要通过数字签名来保证文档的真实性和有效性;甚至于人们日常使用频繁的电子邮件,当涉及重要内容时,也需要通过数字签名技术来对邮件的发送者进行确认和保证邮件内容未被篡改,并且邮件的发送者也不能对发出的邮件进行否认。由此可见,数字签名技术早已深入应用到国家的政治、军事、经济和人们生活中的各个方面,并将在国家数字 化进程中发挥越来越重要的作用。
4 JDK支持的信息摘要算法JDK8原生算法列表,可参第一篇博文: https://blog.csdn.net/yunyun1886358/article/details/128592503#311_JDK_Provider_63
5 Bouncy Castle 支持的信息摘要算法Bouncy Castle算法列表,可参第一篇博文:
https://editor.csdn.net/md/?articleId=128592503#323_Bouncy_Castle_Provider_568
下面的代码将JDK提供的几种数字签名算法用枚枚举类进行了封装。
首先使用ktool生成密钥库,并导出公钥证书:
keytool -genkeypair -alias testing-keys -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore testing-keystore.p12 -validity 36500
keytool -list -v -keystore testing-keystore.p12
keytool -export -keystore testing-keystore.p12 -alias testing-keys -file testing-ca.cer -rfc
package com.qupeng.crypto.algorithm.oop;
import org.junit.Assert;
import org.junit.Test;
public class DigitalSignatureAlgorithmTest {@Test
public void sign() throws Exception {String signatureStr = DigitalSignatureAlgorithm.MD2withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.MD2withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.MD5withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.MD5withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.SHA1withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.SHA1withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.SHA1withDSA.signByPrivateKeyFromFile("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.SHA1withDSA.verifySignatureByPublicKeyFromFile("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.SHA256withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.SHA256withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.SHA224withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.SHA224withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.SHA384withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.SHA384withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.SHA512withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.SHA512withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.RIPEMD128withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.RIPEMD128withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
signatureStr = DigitalSignatureAlgorithm.RIPEMD160withRSA.signByPrivateKeyFromKeyStore("1234567890");
Assert.assertTrue(DigitalSignatureAlgorithm.RIPEMD160withRSA.verifySignatureByPublicKeyFromCA("1234567890", signatureStr));
}
}
package com.qupeng.crypto.algorithm.oop;
import com.qupeng.crypto.util.CryptoUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.util.Base64;
public enum DigitalSignatureAlgorithm {MD2withRSA("MD2withRSA", "RSA", "default"),
MD5withRSA("MD5withRSA", "RSA", "default"),
SHA1withRSA("SHA1withRSA", "RSA", "default"),
SHA1withDSA("SHA1withDSA", "DSA", "default"),
SHA256withRSA("SHA256withRSA", "RSA", "default"),
SHA224withRSA("SHA224withRSA", "RSA", "BC"),
SHA384withRSA("SHA384withRSA", "RSA", "BC"),
SHA512withRSA("SHA512withRSA", "RSA", "BC"),
RIPEMD128withRSA("RIPEMD128withRSA", "RSA", "BC"),
RIPEMD160withRSA("RIPEMD160withRSA", "RSA", "BC");
static {Security.addProvider(new BouncyCastleProvider());
}
private String algorithm;
private String encryptionAlgorithm;
private String providerName;
DigitalSignatureAlgorithm(String algorithm, String encryptionAlgorithm, String providerName) {this.algorithm = algorithm;
this.encryptionAlgorithm = encryptionAlgorithm;
this.providerName = providerName;
}
public String signByPrivateKeyFromKeyStore(String plainText) throws Exception {PrivateKey privateKey = CryptoUtils.getPrivateKeyFromKeyStore("testing-keys", "123456", "PKCS12");
return sign(plainText, privateKey);
}
public String signByPrivateKeyFromFile(String plainText) throws Exception {String privateKeyStr = CryptoUtils.readPrivateKeyFromFile(this.encryptionAlgorithm);
PrivateKey privateKey = CryptoUtils.getPrivateKeyByStr(this.encryptionAlgorithm, privateKeyStr);
return sign(plainText, privateKey);
}
public String sign(String plainText, PrivateKey privateKey) throws Exception {Signature signature;
if ("default".equals(providerName)) {signature = Signature.getInstance(this.algorithm);
} else {signature = Signature.getInstance(this.algorithm, providerName);
}
signature.initSign(privateKey);
signature.update(plainText.getBytes());
byte[] signatureBytes = signature.sign();
String cipherText = Base64.getEncoder().encodeToString(signatureBytes);
System.out.println(String.format("%s plain text: %s ->digital signature: %s", this.algorithm, plainText, cipherText));
return cipherText;
}
public boolean verifySignatureByPublicKeyFromCA(String plainText, String signatureStr) throws Exception {PublicKey publicKey = CryptoUtils.getPublicKeyFromCA("X.509");
return verifySignature(plainText, signatureStr, publicKey);
}
public boolean verifySignatureByPublicKeyFromFile(String plainText, String signatureStr) throws Exception {String publicKeyStr = CryptoUtils.readPublicKeyFromFile(this.encryptionAlgorithm);
PublicKey publicKey = CryptoUtils.getPublicKeyByStr(this.encryptionAlgorithm, publicKeyStr);
return verifySignature(plainText, signatureStr, publicKey);
}
public boolean verifySignature(String plainText, String signatureStr, PublicKey publicKey) throws Exception {Signature signature;
if ("default".equals(providerName)) {signature = Signature.getInstance(this.algorithm);
} else {signature = Signature.getInstance(this.algorithm, providerName);
}
signature.initVerify(publicKey);
signature.update(plainText.getBytes());
boolean verifyResult = signature.verify(Base64.getDecoder().decode(signatureStr));
System.out.println(String.format("Signature: %s is %s", signatureStr, verifyResult ? "valid" : "invalid"));
return verifyResult;
}
}
package com.qupeng.crypto.algorithm.oop;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
public class CryptoUtils {public final static Path RSA_PUBLIC_KEY_FILE_PATH = Paths.get("C:", "Users", "Administrator", "IdeaProjects", "javasedemo", "src", "main", "resources", "rsa-public-key.txt");
public final static Path RSA_PRIVATE_KEY_FILE_PATH = Paths.get("C:", "Users", "Administrator", "IdeaProjects", "javasedemo", "src", "main", "resources", "rsa-private-key.txt");
public final static Path DSA_PUBLIC_KEY_FILE_PATH = Paths.get("C:", "Users", "Administrator", "IdeaProjects", "javasedemo", "src", "main", "resources", "dsa-public-key.txt");
public final static Path DSA_PRIVATE_KEY_FILE_PATH = Paths.get("C:", "Users", "Administrator", "IdeaProjects", "javasedemo", "src", "main", "resources", "dsa-private-key.txt");
private static Path keyStorePath;
private static Path certificatePath;
static {try {keyStorePath = Paths.get(DigitalSignatureAlgorithm.class.getClassLoader().getResource("testing-keystore.p12").toURI());
certificatePath = Paths.get(DigitalSignatureAlgorithm.class.getClassLoader().getResource("testing-ca.cer").toURI());
} catch (URISyntaxException e) {e.printStackTrace();
}
}
public static String readPublicKeyFromFile(String algorithm) throws IOException {return readKeyStrFromFile("DSA".equalsIgnoreCase(algorithm) ? DSA_PUBLIC_KEY_FILE_PATH : RSA_PUBLIC_KEY_FILE_PATH);
}
public static String readPrivateKeyFromFile(String algorithm) throws IOException {return readKeyStrFromFile("DSA".equalsIgnoreCase(algorithm) ? DSA_PRIVATE_KEY_FILE_PATH : RSA_PRIVATE_KEY_FILE_PATH);
}
public static String readKeyStrFromFile(Path keyFilePath) throws IOException {try (FileChannel keyFileChannel = FileChannel.open(keyFilePath, StandardOpenOption.READ)) {byte[] bytes = new byte[0];
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
int readCount = keyFileChannel.read(byteBuffer);
while (0< readCount) {byteBuffer.flip();
int length = bytes.length;
bytes = Arrays.copyOf(bytes, bytes.length + readCount);
System.arraycopy(byteBuffer.array(), 0, bytes, length, readCount);
byteBuffer.clear();
readCount = keyFileChannel.read(byteBuffer);
}
String keyStr = new String(bytes);
return keyStr;
}
}
public static PublicKey getPublicKeyFromCA(String certificationType) throws Exception {CertificateFactory certificateFactory = CertificateFactory.getInstance(certificationType);
try (FileInputStream in = new FileInputStream(certificatePath.toFile())) {Certificate certificate = certificateFactory.generateCertificate(in);
return certificate.getPublicKey();
}
}
public static PublicKey getPublicKeyFromKeyStore(String alias, String password, String keyStoreType) throws Exception {try (FileInputStream is = new FileInputStream(keyStorePath.toFile())) {KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(is, password.toCharArray());
return keyStore.getCertificate(alias).getPublicKey();
}
}
public static PrivateKey getPrivateKeyFromKeyStore(String alias, String password, String keyStoreType) throws Exception {try (FileInputStream is = new FileInputStream(keyStorePath.toFile())) {KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(is, password.toCharArray());
return (PrivateKey) keyStore.getKey(alias, password.toCharArray());
}
}
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网站名称:Java加解密(七)数字签名-创新互联
标题来源:http://lswzjz.com/article/ccheig.html