基本概念:
数字签名,顾名思义,就类似于一种写在纸上的普通的物理签名.
简单说就是将文件内容进行hash散列(消息摘要),信息发送者对散列后的字符串使用私钥加密,得到的最终字符串就是签名。然后将得到的签名字符串添加到文件信息的后面一同发送出去。接收者获取到文件信息和签名后,使用公钥对签名进行解密,就得到文件内容和加密后的hash散列。此时,他可以对获取到的文件内容做hash散列,与签名中的hash散列进行匹对,从而鉴别出最终获取信息的真伪.
整个过程可以用如下图解理解.
算法种类:
常见的几种KeyPairGenerator算法
KeyPairGenerator
Algorithms
(除了指出,这些类创建Key.getAlgorithm()返回标准算法名称的密钥.)
生成KeyPairGenerator实例时可以指定本节中的算法名称.
Algorithm Name | Description |
DiffieHellman | 为Diffie-Hellman密钥协商算法生成密钥对。 注意:key.getAlgorithm()将返回“DH”而不是“DiffieHellman”。 |
DSA | Generates keypairs for the Digital Signature Algorithm. |
RSA | Generates keypairs for the RSA algorithm (Signature/Cipher). |
EC | Generates keypairs for the Elliptic Curve algorithm. |
常见的签名算法
Signature
Algorithms
生成Signature实例时可以指定本节中的算法名称。
Algorithm Name | Description |
NONEwithRSA | RSA签名算法在执行RSA操作之前不使用摘要算法(例如MD5 /SHA1)。 |
MD2withRSA MD5withRSA | 使用MD2 / MD5摘要算法和RSA来创建和验证 |
SHA1withRSA SHA224withRSA SHA256withRSA SHA384withRSA SHA512withRSA | 使用 SHA- * 的签名算法和OSI Interoperability Workshop中定义的RSA加密算法 |
NONEwithDSA | FIPS PUB 186-2中定义的数字签名算法。 数据长度必须是20个字节。 这个算法也被称为rawDSA。 |
SHA1withDSA SHA224withDSA SHA256withDSA | DSA签名算法使用SHA-1,SHA-224或SHA-256摘要算法来创建和验证 |
NONEwithECDSA SHA1withECDSA SHA224withECDSA SHA256withECDSA SHA384withECDSA SHA512withECDSA (ECDSA) | ECDSA签名算法。 |
<digest>with<encryption> | 使用这个名称为具有特定消息摘要(如MD2或MD5)和算法(如RSA或DSA)的签名算法创建一个名称,就像本节中明确定义的标准名称(MD2withRSA等)一样 上) |
code实例:
DataSecurity:
package com.fitc.soldier.service.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.apache.commons.codec.binary.Hex;
public class DataSecurity {
private static String testContent="这是一个测试文本!!!";
public static void main(String[] args) {
// localKeyPairMethod(testContent);
// localPrivatKeyMethod(testContent);
generatorKey(testContent);
}
/**
* 加载本地密钥对
*/
public static void localKeyPairMethod(String Content){
try {
File file = new File("ca.key");
FileOutputStream fileOutputStream = new FileOutputStream(file);
//加载本地KeyPair 密钥对
KeyPair keyPair = KeyPairUtil.generatorkeyPair();
KeyPairUtil.storeKeyPair(keyPair, fileOutputStream);
KeyPair localFileKeyPair = KeyPairUtil.localFileKeyPair(new FileInputStream(file));
PublicKey publicKey = localFileKeyPair.getPublic();
PrivateKey privateKey = localFileKeyPair.getPrivate();
DataSignaturer dataSignaturer=new DataSignaturer(privateKey, publicKey);
byte[] sign = dataSignaturer.sign(Content.getBytes());
System.out.println("签名:\t"+Hex.encodeHexString(sign));
System.out.println("验证签名结果\t"+dataSignaturer.verifySign(Content.getBytes(), sign));
} catch (FileNotFoundException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
/**
* 加载本地私钥/公钥 对数据实现签名和验证
*/
public static void localPrivatKeyMethod(String Content){
try {
File private_key_File = new File("private_key.key");
File public_key_File = new File("public_key.key");
FileOutputStream private_keytStream = new FileOutputStream(private_key_File);
FileOutputStream public_keyStream = new FileOutputStream(public_key_File);
//生成本地密钥 分别存储本地
KeyPair keyPair = KeyPairUtil.generatorkeyPair();
KeyPairUtil.storeKeyPair(keyPair, private_keytStream, public_keyStream);
//加载本地密钥文件
PrivateKey loadFilePrivateKey = KeyPairUtil.loadFilePrivateKey(new FileInputStream(private_key_File));
PublicKey loadFilePublicKey = KeyPairUtil.loadFilePublicKey(new FileInputStream(public_key_File));
DataSignaturer dataSignaturer=new DataSignaturer(loadFilePrivateKey, loadFilePublicKey);
byte[] sign = dataSignaturer.sign(Content.getBytes());
System.out.println("签名:\t"+Hex.encodeHexString(sign));
System.out.println("签名:\t"+StringHelper.encoderBase64(sign));
System.out.println("验证签名结果\t"+dataSignaturer.verifySign(Content.getBytes(), sign));
} catch (FileNotFoundException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
/**
* 使用密钥对 构建公私钥 对数据进行签名和验证
*/
public static void generatorKey(String Content){
try {
//生成密钥
KeyPair keyPair = KeyPairUtil.generatorkeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
KeyFactory keyFactory=KeyFactory.getInstance(KeyPairUtil.KEY_ALGORITHM);
// 将生成的密钥 按照PKCS#8标准作为密钥规范管理的编码格式 转换成 密钥
// 在 Java 语言中,此类表示基于在 PKCS 8 标准中定义的 ASN.1 类型的编码密钥
PKCS8EncodedKeySpec pkcs8encodedkeyspec=new PKCS8EncodedKeySpec(privateKey.getEncoded());
PrivateKey generatePrivate = keyFactory.generatePrivate(pkcs8encodedkeyspec);
//使用密钥签名 SHA1withRSA 签名算法
Signature signature=Signature.getInstance("SHA1withRSA");
signature.initSign(generatePrivate);
signature.update(Content.getBytes());
byte[] sign = signature.sign();
System.out.println("原文内容:\t"+Content);
System.out.println("签名结果:\t"+Hex.encodeHexString(sign));
//创建公钥 ,使用公钥验证签名 X509EncodedKeySpec ASN.1 类型的编码密钥
X509EncodedKeySpec x509encodedkeyspec=new X509EncodedKeySpec(publicKey.getEncoded());
PublicKey generatePublic = keyFactory.generatePublic(x509encodedkeyspec);
signature.initVerify(generatePublic);
signature.update(Content.getBytes());
System.out.println("原文内容:\t"+Content);
System.out.println("验证签名是否通过:\t"+signature.verify(sign));
} catch (NoSuchAlgorithmException | InvalidKeySpecException | SignatureException | InvalidKeyException e) {
e.printStackTrace();
}
}
}
KeyPairUtil(密钥对的生成或者 加载本地密钥文件):
package com.fitc.soldier.service.common;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* <pre>
*公钥,私钥生成工具类</br>
*可以又 KeyPairGenerator 秘钥对生成器生成然后保存到本地</br>
*或者直接读取本地的密钥文件
*</pre>
*/
public class KeyPairUtil {
// 可以用DSA,也可以用RSA
public static final String KEY_ALGORITHM="RSA";
/**
* 从输入流中获取KeyPair 秘钥对 对象
* @param keyPairStream 输入流
* @return
*/
public static KeyPair localFileKeyPair(InputStream keyPairStream){
if (keyPairStream==null) {
System.out.println("指定的输入流=null!因此无法读取KeyPair!");
return null;
}
ObjectInputStream objectInputStream=null;
try {
objectInputStream=new ObjectInputStream(keyPairStream);
KeyPair keyPair =(KeyPair)objectInputStream.readObject();
objectInputStream.close();
return keyPair;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (objectInputStream!=null) {
try {
objectInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
/**
* 从本地文件加载公钥
* @param publicKeyInputStream 输入流
* @return
*/
public static PublicKey loadFilePublicKey(InputStream publicKeyInputStream){
ObjectInputStream objectInputStream=null;
try {
objectInputStream=new ObjectInputStream(publicKeyInputStream);
PublicKey publickey = (PublicKey)objectInputStream.readObject();
objectInputStream.close();
return publickey;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (objectInputStream!=null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* 从本地文件加载私钥
* @param PrivateKeyInputStream 输入流
* @return
*/
public static PrivateKey loadFilePrivateKey(InputStream PrivateKeyInputStream){
ObjectInputStream objectInputStream=null;
try {
objectInputStream=new ObjectInputStream(PrivateKeyInputStream);
PrivateKey privatekey = (PrivateKey)objectInputStream.readObject();
objectInputStream.close();
return privatekey;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (objectInputStream!=null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* 将整个KeyPair密钥对 对象存在本地文件
* * @param keyPair 公钥私钥对对象
* @param out 输出流
* @return
*/
public static boolean storeKeyPair(KeyPair keyPair,OutputStream out){
if ((keyPair == null) || (out == null)) {
System.out.println("keyPair or OutputStream is null ");
return false;
}
ObjectOutputStream objectOutputStream=null;
try {
objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(keyPair);
objectOutputStream.close();
return true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
/**
*将公钥,私钥分开存储在IO流中
* @param keyPair 公钥私钥对对象
* @param out 输出流
* @return
*/
public static boolean storeKeyPair(KeyPair keyPair,OutputStream privateKeyOut,OutputStream publicKeyOut){
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey=keyPair.getPublic();
boolean resut=false;
if (keyPair==null||privateKeyOut==null||publicKeyOut==null) {
System.out.println("指定的IO流或者密钥对 对象为null");
return resut;
}
ObjectOutputStream privateKeyStream = null;
ObjectOutputStream publicKeyStream = null;
try {
privateKeyStream = new ObjectOutputStream(privateKeyOut);
privateKeyStream.writeObject(privateKey);
publicKeyStream = new ObjectOutputStream(publicKeyOut);
publicKeyStream.writeObject(publicKey);
resut = true;
privateKeyStream.close();
publicKeyStream.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (privateKeyStream!=null&&publicKeyStream!=null) {
try {
privateKeyStream.close();
publicKeyStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return resut;
}
/**
* 生成KeyPair公钥私钥对
*
* @return
*/
public static KeyPair generatorkeyPair(){
try {
KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGenerator.initialize(1024);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
DataSignaturer(实现签名和验证):
package com.fitc.soldier.service.common;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**<pre>
* 实现数字签名的类
* X509EncodedKeySpec和PKCS8EncodedKeySpec两个类在加密解密环节中经常会用到。
* 密钥很可能会以二进制方式存储于文件中,由程序来读取。这时候,就需要通过这两个类将文件中的字节数组
* 读出转换为密钥对象。
* </pre>
*/
public class DataSignaturer {
/**
* 私钥
*/
private PrivateKey privateKey;
/**
* 公钥
*/
private PublicKey publicKey;
/**
* key 工厂
*/
private KeyFactory keyFactory;
public DataSignaturer(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException {
super();
this.privateKey = privateKey;
this.publicKey = publicKey;
this.keyFactory=KeyFactory.getInstance(privateKey.getAlgorithm());
}
/**
* 进行数字签名
* @param data
* @return
*/
public byte[] sign(byte[] resoure){
if (privateKey==null||publicKey==null) {
System.out.println("privateKey or publicKey is null ");
return null;
}
try {
// 初始化签名算法
// String signatuureAlgorithm="";
// if (algorithm.equals("RSA")) {
// signatuureAlgorithm="MD5withRSA";
// signatuureAlgorithm="SHA1withRSA";
// }else if (algorithm.equals("DSA")) {
// signatuureAlgorithm="SHA1withDSA";
// }else {
// signatuureAlgorithm="SHA1withECDSA";
// }
// 或者为了方便统一使用SHA1with**的签名算法
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
PrivateKey privatekey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature=Signature.getInstance("SHA1with"+privateKey.getAlgorithm());
signature.initSign(privatekey);
signature.update(resoure);
return signature.sign();
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
/**
* 验证数字签名
* @param data
* @param signature
* @return
*/
public boolean verifySign(byte[] resoure, byte[] signature) {
if (privateKey == null || publicKey == null) {
System.out.println("privateKey or publicKey is null ");
return false;
}
try {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec (publicKey.getEncoded());
PublicKey publickey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature verifySign = Signature.getInstance("SHA1with"+publicKey.getAlgorithm());
verifySign.initVerify(publickey);
verifySign.update(resoure);
return verifySign.verify(signature);
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | InvalidKeySpecException e) {
e.printStackTrace();
}
return false;
}
}
StringHelper(Base64编码工具类):
package com.fitc.soldier.service.common;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
*
*Base64的编解码
*将密钥编码成Base64 以便传输和查看
*/
public class StringHelper {
/**
* BASE64Encoder 加密
* @param data 要加密的数据
* @return 加密后的字符串
*/
public static String encoderBase64(byte[] data){
if (data==null||data.length==0) {
return "";
}
BASE64Encoder base64Encoder = new BASE64Encoder();
return base64Encoder.encode(data);
}
/**
* BASE64Decoder 解密
* @param data 要解密的字符串
* @return 解密后的byte[]
* @throws Exception
*/
public static byte[] decoderBase64(String encodeString) throws IOException{
if (StringUtils.isEmpty(encodeString)) {
return null;
}
BASE64Decoder base64Encoder = new BASE64Decoder();
return base64Encoder.decodeBuffer(encodeString);
}
}
console 输出:
总结.
在使用签名算法对数据进行数据签名和验证时,主要步骤
1.创建Signature对象
Signature verifySign = Signature.getInstance(String algorithm);//algorith为前面表格中的值
2.初始化:
Signature .initVerify(PublicKey publicKey)/Signature .initSign(PrivateKey privateKey)
3.填充数据:
Signature.update(byte[] data);
签名和验证
4.Signature.sign() 和 verify(byte[] signature)方法.