Java数字签名实现

一、历史背景

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

Java数字签名实现插图

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

Java数字签名实现插图2

二、数字签名

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

三、算法举例

1、MD5withRSA

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);
    }
}

执行结果:

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

2、SHA1withDSA

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);
    }
}

执行结果:

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

3、SHA1withECDSA

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);
    }
}

执行结果:

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

发表评论