天天看點

Java加解密之數字簽名算法

1. 簡介

數字簽名(英語:Digital Signature,又稱公鑰數字簽名)是一種功能類似寫在紙上的普通簽名、但是使用了公鑰加密領域的技術,以用于鑒别數字資訊的方法。一套數字簽名通常會定義兩種互補的運算,一個用于簽名,另一個用于驗證。法律用語中的電子簽章與數字簽名代表之意義并不相同。電子簽章指的是依附于電子檔案并與其相關聯,用以辨識及确認電子檔案簽署人身份、資格及電子檔案真僞者;數字簽名則是以數學算法或其他方式運算對其加密而形成的電子簽章。意即并非所有的電子簽章都是數字簽名。

    數字前面的簡單原理如下:

Java加解密之數字簽名算法

通常會使用公鑰加密,用私鑰解密。而在數字簽名中,會使用私鑰加密(相當于生成簽名),公鑰解密(相當于驗證簽名)。

可以直接對消息進行簽名(即使用私鑰加密,此時加密的目的是為了簽名,而不是保密),驗證者用公鑰正确解密消息,如果和原消息一緻,則驗證簽名成功。但通常會對消息的散列值簽名,因為通常散列值的長度遠小于消息原文,使得簽名(非對稱加密)的效率大大提高。注意,計算消息的散列值不是數字簽名的必要步驟。

在實際使用中,我們既想加密消息,又想簽名,是以要對加密和簽名組合使用,比如TLS就組合了加密和簽名。

數字簽名應用了公鑰密碼領域使用的單向函數原理。單向函數指的是正向操作非常簡單,而逆向操作非常困難的函數,比如大整數乘法。這種函數往往提供一種難解或懷疑難解的數學問題。目前,公鑰密碼領域具備實用性的三個懷疑難解問題為:質數分解,離散對數和橢圓曲線問題。

    注:數字簽名是為了驗證資料完整性、認證資料來源、抗否認等。

2. 數字簽名算法--RSA

    RSA也可以用來為一個消息署名。

    RSA代碼實作:

package com.bity.digitalsignature;

import org.apache.commons.codec.binary.Hex;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * <p>Title: DsRsa</p >
 * <p>Description: RSA數字前面 </p >
 * <p>Company: http://www.agree.com</p >
 * <p>Project: security</p >
 *
 * @author <a href="mailto:[email protected]">WEIQI</a>
 * @version 1.0
 * @date 2022/4/28 14:21
 */
public class DsRsa {
    
    private static final String src = "I'm RSA digital signature security info";
    
    public static void main(String[] args) {
        jdkRsa();
    }
    
    /**
     * JDK-RSA數字簽名
     *
     * @author: <a href="mailto:[email protected]">WEIQI</a>
     * @date: 2022/4/28 14:23
     */
    private static void jdkRsa() {
        try {
            //1.初始化密鑰
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(512);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
            
            //2.執行簽名
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Signature signature = Signature.getInstance("MD5withRSA");
            signature.initSign(privateKey);
            signature.update(src.getBytes());
            byte[] result = signature.sign();
            System.out.println("jdk rsa sign : " + Hex.encodeHexString(result));
            
            //3.驗證簽名
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            signature = Signature.getInstance("MD5withRSA");
            signature.initVerify(publicKey);
            signature.update(src.getBytes());
            boolean bool = signature.verify(result);
            System.out.println("jdk rsa verify : " + bool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}      

運作結果:

jdk rsa sign : 7a48c41e5a5d0d2afe60fd05d1219af78f4907ac79eeb0db50ea3ad5646a9b2f8303109b5f040f59e85d3f08cc25c34a6c3f03f5d23fb1c9f04c4dc5ceaf3b9c
jdk rsa verify : true      

RSA數字簽名圖示:

Java加解密之數字簽名算法

3. 數字簽名算法--DSA

    數字簽名算法(DSA)是用于數字簽名的聯邦資訊處理标準之一,基于模算數和離散對數的複雜度。DSA是Schnorr和ElGamal簽名方案的變體。

    DSA代碼實作:

package com.bity.digitalsignature;

import org.apache.commons.codec.binary.Hex;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * <p>Title: DsDsa</p >
 * <p>Description: DSA算法實作 </p >
 * <p>Company: http://www.agree.com</p >
 * <p>Project: security</p >
 *
 * @author <a href="mailto:[email protected]">WEIQI</a>
 * @version 1.0
 * @date 2022/4/28 14:34
 */
public class DsDsa {
    
    private static final String src = "I'm DSA digital signature security info";
    
    public static void main(String[] args) {
        jdkDsa();
    }
    
    /**
     * JDK-DSA數字簽名實作
     *
     * @author: <a href="mailto:[email protected]">WEIQI</a>
     * @date: 2022/4/28 14:36
     */
    private static void jdkDsa() {
        try {
            //1.初始化密鑰
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
            keyPairGenerator.initialize(512);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            DSAPublicKey dsaPublicKey = (DSAPublicKey) keyPair.getPublic();
            DSAPrivateKey dsaPrivateKey = (DSAPrivateKey)keyPair.getPrivate();
        
            //2.執行簽名
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(dsaPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("DSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Signature signature = Signature.getInstance("SHA1withDSA");
            signature.initSign(privateKey);
            signature.update(src.getBytes());
            byte[] result = signature.sign();
            System.out.println("jdk dsa sign : " + Hex.encodeHexString(result));
        
            //3.驗證簽名
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(dsaPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("DSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            signature = Signature.getInstance("SHA1withDSA");
            signature.initVerify(publicKey);
            signature.update(src.getBytes());
            boolean bool = signature.verify(result);
            System.out.println("jdk dsa verify : " + bool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}      

運作結果:

jdk dsa sign : 302c02140e84921c75543572355259ecd19a099c1a9632ab0214452ba60580452aa21e0996ee358e287eb41207d4
jdk dsa verify : true      

DSA數字簽名圖示:

Java加解密之數字簽名算法

4. 數字簽名算法--ECDSA

    橢圓曲線數字簽名算法(英語:Elliptic Curve Digital Signature Algorithm,縮寫作 ECDSA)是一種基于橢圓曲線密碼學的公開金鑰加密算法。1985年,Koblitz和Miller把數字簽名算法移植到橢圓曲線上,橢圓曲線數字簽名算法由此誕生。

    ECDSA代碼實作:

package com.bity.digitalsignature;

import org.apache.commons.codec.binary.Hex;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * <p>Title: DsEcdsa</p >
 * <p>Description: ECDSA數字簽名實作 </p >
 * <p>Company: http://www.agree.com</p >
 * <p>Project: security</p >
 *
 * @author <a href="mailto:[email protected]">WEIQI</a>
 * @version 1.0
 * @date 2022/4/28 14:41
 */
public class DsEcdsa {
    private static final String src = "I'm ECDSA digital signature security info";
    
    public static void main(String[] args) {
        jdkEcdsa();
    }
    
    /**
     * JDK-ECDSA數字簽名實作
     *
     * @author: <a href="mailto:[email protected]">WEIQI</a>
     * @date: 2022/4/28 14:42
     */
    private static void jdkEcdsa() {
        try {
            //1.初始化密鑰
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
            keyPairGenerator.initialize(256);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            ECPublicKey ecPublicKey = (ECPublicKey)keyPair.getPublic();
            ECPrivateKey ecPrivateKey = (ECPrivateKey)keyPair.getPrivate();
        
            //2.執行簽名
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(ecPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Signature signature = Signature.getInstance("SHA1withECDSA");
            signature.initSign(privateKey);
            signature.update(src.getBytes());
            byte[] result = signature.sign();
            System.out.println("jdk ecdsa sign : " + Hex.encodeHexString(result));
        
            //3.驗證簽名
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(ecPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("EC");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            signature = Signature.getInstance("SHA1withECDSA");
            signature.initVerify(publicKey);
            signature.update(src.getBytes());
            boolean bool = signature.verify(result);
            System.out.println("jdk ecdsa verify : " + bool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}      

運作結果:

jdk ecdsa sign : 3046022100aeac2ff850c5ccb67d50fc65a3775951f6c73ab6708e0f0519214c71290abac9022100f48828aeff296b7f1aa1dd0d425284cfb11bc3cf148fa4112a8f50d32aa18a74
jdk ecdsa verify : true      

ECDSA數字簽名圖示:

Java加解密之數字簽名算法

我們日常使用的微軟的産品(比如作業系統密鑰)就是使用ECDSA實作的。

5. 總結

附加

繼續閱讀