basic concept:
A digital signature, as the name suggests, is similar to an ordinary physical signature written on paper.
Simply put, the content of the file is hashed (message digest), and the message sender encrypts the hashed string with the private key, and the final string obtained is the signature. Then add the obtained signature string to the back of the file information and send it together. After the receiver obtains the file information and signature, it decrypts the signature with the public key to obtain the file content and the encrypted hash. At this point, he can hash the content of the obtained file and match it with the hash in the signature, so as to identify the authenticity of the final obtained information.
The whole process can be understood with the following diagram.
Algorithm Type:
Several common KeyPairGenerator algorithms
KeyPairGenerator
Algorithms
(Except as noted, these classes create keys whose Key.getAlgorithm() returns the standard algorithm name.)
The algorithm name in this section can be specified when generating a KeyPairGenerator instance.
Algorithm Name | Description |
---|---|
DiffieHellman | Generate a key pair for the Diffie-Hellman key agreement algorithm. Note: key.getAlgorithm() will return "DH" instead of "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. |
Common Signature Algorithms
Signature
Algorithms
The algorithm name in this section can be specified when generating a Signature instance.
Algorithm Name | Description |
---|---|
NONEwithRSA | The RSA signature algorithm does not use a digest algorithm (eg MD5/SHA1) before performing the RSA operation. |
MD2withRSA MD5withRSA |
Use MD2/MD5 digest algorithm and RSA to create and verify |
SHA1withRSA SHA224withRSA SHA256withRSA SHA384withRSA SHA512withRSA |
The signature algorithm using SHA-* and the RSA encryption algorithm defined in the OSI Interoperability Workshop |
NONEwithDSA | Digital signature algorithm as defined in FIPS PUB 186-2. The data length must be 20 bytes. This algorithm is also known as rawDSA. |
SHA1withDSA SHA224withDSA SHA256withDSA |
DSA signature algorithm uses SHA-1, SHA-224 or SHA-256 digest algorithm to create and verify |
NONEwithECDSA SHA1withECDSA SHA224withECDSA SHA256withECDSA SHA384withECDSA SHA512withECDSA (ECDSA) |
ECDSA signature algorithm. |
<digest>with<encryption> | Use this name to create a name for a signature algorithm with a specific message digest (such as MD2 or MD5) and algorithm (such as RSA or DSA), just like the well-defined standard names (MD2withRSA, etc.) in this section above) |
code example:
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 (generate key pair or load local key file):
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 (implements signing and verification):
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 encoding tool class):
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 output:
总结.
在使用签名算法对数据进行数据签名和验证时,主要步骤
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)方法.
---------------------------------------------------分割线-----------------------------------------------