數字簽名,顧名思義,就相似於一種寫在紙上的普通的物理簽名.html
簡單說就是將文件內容進行hash散列(消息摘要),信息發送者對散列後的字符串使用私鑰加密,獲得的最終字符串就是簽名。而後將獲得的簽名字符串添加到文件信息的後面一同發送出去。接收者獲取到文件信息和簽名後,使用公鑰對簽名進行解密,就獲得文件內容和加密後的hash散列。此時,他能夠對獲取到的文件內容作hash散列,與簽名中的hash散列進行匹對,從而鑑別出最終獲取信息的真僞.java
整個過程能夠用以下圖解理解.git
常見的幾種KeyPairGenerator算法算法
KeyPairGenerator
Algorithms(除了指出,這些類建立Key.getAlgorithm()返回標準算法名稱的密鑰.)apache
生成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。this |
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等)同樣 上) |
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)方法.
---------------------------------------------------分割線-----------------------------------------------