Java implementation of digital signature (RSA, DSA)

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

KeyPairGeneratorAlgorithms

(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

SignatureAlgorithms

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.
Note: "ECDSA" is an obscure name for the "SHA1withECDSA" algorithm and should not be used. The official name "SHA1withECDSA" should be used.

<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)方法.

---------------------------------------------------分割线-----------------------------------------------

 

 

参考:https://www.cnblogs.com/huangzijian/p/6347293.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325451377&siteId=291194637