Java数字签名实现

一、历史背景

父辈或更早的前辈们经常会有一个印戳,上面刻有名字等证明身份的图案,每次办事的时候经常会使用这个戳去盖而不是手写签名。

Java数字签名实现插图

到后来这方方式逐渐被淘汰掉了,改成了手写签名。为什么会被淘汰?主要原因是这样的形式的戳比较容易被伪造,只要找到刻章工匠们就可以做出一模一样的戳且无法辨认真实性。使用手写签名之后安全性和不可抵赖性得到了提高。

Java数字签名实现插图2

二、数字签名

  • 定义:带有密钥(公钥、私钥)的消息摘要算法
  • 作用:验证数据完整性、认证数据来源、不可抵赖
  • 说明:遵循使用私钥进行签名,使用公钥对签名的数据进行验证的规则。
  • 常用的数字签名算法有:RSA、DSA、ECDSA

三、算法举例

1、MD5withRSA

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
 
/**
 * RMD5withRSA数字签名使用例子
 * 签名,验签
public class MD5withRSADemo {
 
    private static final char[] HEXES = {
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f'
    };
 
    /**
     * byte数组 转换成 16进制小写字符串
     */    public static String bytes2Hex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        StringBuilder hex = new StringBuilder();
        for (byte b : bytes) {
            hex.append(HEXES[(b >> 4) & 0x0F]);
            hex.append(HEXES[b & 0x0F]);
        }
        return hex.toString();
    }
 
    public static void main(String[] args) throws Exception {
        // 加密字符串
        String src = "EasyJava";
 
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(512);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
 
        // 初始化公钥和私钥
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        System.out.println("公钥:" + Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded()));
        System.out.println("私钥:" + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded()));
 
        // 私钥签名
        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("utf-8"));
        byte[] signResult = signature.sign();
        System.out.println("签名结果:" + bytes2Hex(signResult));
 
        // 公钥验证
        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 verifyResult = signature.verify(signResult);
        System.out.println("验证结果:" + verifyResult);
    }
}

执行结果:

1
2
3
4
公钥:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAINcPA9dHlPQn17iMwTPJxU5Hyd/OecYjW8NLP8GH5e0hpfax51JWQV9C5sKBzahjR5/49hVv3GbGoqYg2NVGLcCAwEAAQ==
私钥:MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAg1w8D10eU9CfXuIzBM8nFTkfJ3855xiNbw0s/wYfl7SGl9rHnUlZBX0LmwoHNqGNHn/j2FW/cZsaipiDY1UYtwIDAQABAkBT83J+7ygesMnpve6VOD7mFWePuoOq6coaGvWS1rsCHHbInY04kvZxlZBTf4Y69qAoVbjqGL0Dh6TouhWyyi6hAiEAwp88R0bHZlG3giz5iN5U1IgWdTk9NG3vcf9NkJSPv+0CIQCsyZQGsZn8pkBnOFPc675D4HqvYveLA0A6pVxqD+C+swIgDncVXNrW4TT7pbJADbswpOdEfAv4D5iILnniQve3w3UCIEeq6KIKIlO/5XCq1WYJxgZDYr/CbeiT9Z0dn4JCibavAiBJ+rwnhiHglwmh0jrpt9PgGkZ+6iy1gr6aHAxlu6jxpw==
签名结果:193cc28101b94bfd13564272d39d1eb53509d6bd621d2eae8a2be20b130663d4c7b1bab92ee0e7ebf7a1e5c2d4cf4ceb09ff7eb5ecc30adf8d51c9bc3909081f
验证结果:true

2、SHA1withDSA

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.security.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
 
/**
 * SHA1withDSA数字签名使用例子
 * 签名,验签
 */public class SHA1withDSADemo {
 
    private static final char[] HEXES = {
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f'
    };
 
    /**
     * byte数组 转换成 16进制小写字符串
     */    public static String bytes2Hex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        StringBuilder hex = new StringBuilder();
        for (byte b : bytes) {
            hex.append(HEXES[(b >> 4) & 0x0F]);
            hex.append(HEXES[b & 0x0F]);
        }
        return hex.toString();
    }
 
    public static void main(String[] args) throws Exception {
        // 加密字符串
        String src = "EasyJava";
 
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(512);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
 
        // 初始化公钥和私钥
        DSAPublicKey dsaPublicKey = (DSAPublicKey) keyPair.getPublic();
        DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) keyPair.getPrivate();
        System.out.println("公钥:" + Base64.getEncoder().encodeToString(dsaPublicKey.getEncoded()));
        System.out.println("私钥:" + Base64.getEncoder().encodeToString(dsaPrivateKey.getEncoded()));
 
        // 私钥签名
        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[] signResult = signature.sign();
        System.out.println("签名结果:" + bytes2Hex(signResult));
 
        // 公钥验证
        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 verifyResult = signature.verify(signResult);
        System.out.println("验证结果:" + verifyResult);
    }
}

执行结果:

1
2
3
4
公钥:MIHxMIGoBgcqhkjOOAQBMIGcAkEA/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFwIVAJYu3cw2nLqOuyYO5rahJtk0bjjFAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykA0QAAkEAztIwIQWBN9CKDdDaf2o/nQxgcfnTXMKZnzCbWpPS0SVoJ4PPVm2KivFqv/vbJYQuQpg3rNlaMbXmf0mtodI4iQ==
私钥:MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFwIVAJYu3cw2nLqOuyYO5rahJtk0bjjFAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykBBYCFGfMn8teKDz6Tvj356ARoniYW4RB
签名结果:302c0214110c199ba3e2aeb580e7b83c54c7b658601f34060214155e9e33c96d93e701639dbbd9247adc949aeb7e
验证结果:true

3、SHA1withECDSA

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
 
/**
 * SHA1withECDSA数字签名使用例子
 * 签名,验签
 */public class SHA1withECDSADemo {
 
    private static final char[] HEXES = {
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f'
    };
 
    /**
     * byte数组 转换成 16进制小写字符串
     */    public static String bytes2Hex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        StringBuilder hex = new StringBuilder();
        for (byte b : bytes) {
            hex.append(HEXES[(b >> 4) & 0x0F]);
            hex.append(HEXES[b & 0x0F]);
        }
        return hex.toString();
    }
 
    public static void main(String[] args) throws Exception {
        // 加密字符串
        String src = "EasyJava";
 
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
        keyPairGenerator.initialize(256);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
 
        // 初始化公钥和私钥
        ECPublicKey ecPublicKey = (ECPublicKey) keyPair.getPublic();
        ECPrivateKey ecPrivateKey = (ECPrivateKey) keyPair.getPrivate();
        System.out.println("公钥:" + Base64.getEncoder().encodeToString(ecPublicKey.getEncoded()));
        System.out.println("私钥:" + Base64.getEncoder().encodeToString(ecPrivateKey.getEncoded()));
 
        // 私钥签名
        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[] signResult = signature.sign();
        System.out.println("签名结果:" + bytes2Hex(signResult));
 
        // 公钥验证
        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 verifyResult = signature.verify(signResult);
        System.out.println("验证结果:" + verifyResult);
    }
}

执行结果:

1
2
3
4
公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMz4FvvFpr8sIOVJE6BbloaLAMMBfUppeTEuH4KNjkAwV5I5yrkSRz9c4AU6LhqSiKn5teMzOfFqDzI+0Jkx4qA==
私钥:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDwwpzQDrwSTAsAtvDI0RnRpWXGu+MzMPFMkoHpD0EDsA==
签名结果:3045022100bdd946f1748a90c9e0241a804b1f75f065d06426d1a7e374003691128c6924db022013eb70b7e0b4fb8d5e104ba954305c339c59234d9e0acc6c27cb7010b384416e
验证结果:true

发表评论

欢迎阅读『Java数字签名实现|Java、开发语言、框架算法、算法|Nick Tan-梓潼Blog』