带密钥的消息摘要算法——数字签名算法

数字签名算法可以看做是一种带有密钥(公钥+私钥)的消息摘要算法,也就是说,数据签名算法是非对称加密算法和消息摘要算法的结合体。该算法包含签名和验证两项操作,遵循 “私钥签名,公钥验证” 的签名/验证方式。

数字签名算法消息传递模型

1、甲方构建密钥对,并能公布公钥给乙方。
2、甲方想乙方发送数据需要附加签名。
3、乙方使用公钥和签名验证数据。

数字签名算法消息传递模型

经典数字签名算法——RSA

RSA数字签名算法主要可以分为:MD、SHA两类。该算法公钥通常要比私钥短。

RSA数字签名算法

RSA数字签名示例:

import java.security.Key;
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;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Before;
import org.junit.Test;

public class RSACoder {
    
    /**
     * 数字签名
     * 密钥算法
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 数字签名
     * 签名/验证算法
     */
    public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";

    /**
     * 私钥
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * RSA密钥长度 默认1024位,
     *  密钥长度必须是64的倍数, 
     *  范围在512至65536位之间。
     */
    private static final int KEY_SIZE = 512;

    /**
     * 签名
     * 
     * @param data
     *            待签名数据
     * @param privateKey
     *            私钥
     * @return byte[] 数字签名
     * @throws Exception
     */
    public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {

        // 转换私钥材料
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);

        // 实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        // 取私钥匙对象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 初始化Signature
        signature.initSign(priKey);

        // 更新
        signature.update(data);

        // 签名
        return signature.sign();
    }

    /**
     * 校验
     * 
     * @param data
     *            待校验数据
     * @param publicKey
     *            公钥
     * @param sign
     *            数字签名
     * 
     * @return boolean 校验成功返回true 失败返回false
     * @throws Exception
     * 
     */
    public static boolean verify(byte[] data, byte[] publicKey, byte[] sign)
            throws Exception {

        // 转换公钥材料
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);

        // 实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        // 生成公钥
        PublicKey pubKey = keyFactory.generatePublic(keySpec);

        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 初始化Signature
        signature.initVerify(pubKey);

        // 更新
        signature.update(data);

        // 验证
        return signature.verify(sign);
    }

    /**
     * 取得私钥
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static byte[] getPrivateKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PRIVATE_KEY);

        return key.getEncoded();
    }

    /**
     * 取得公钥
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static byte[] getPublicKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PUBLIC_KEY);

        return key.getEncoded();
    }

    /**
     * 初始化密钥
     * 
     * @return Map 密钥对儿 Map
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {

        // 实例化密钥对儿生成器
        KeyPairGenerator keyPairGen = KeyPairGenerator
                .getInstance(KEY_ALGORITHM);

        // 初始化密钥对儿生成器
        keyPairGen.initialize(KEY_SIZE);

        // 生成密钥对儿
        KeyPair keyPair = keyPairGen.generateKeyPair();

        // 公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

        // 私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        // 封装密钥
        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);

        return keyMap;
    }

      /**
     * 公钥
     */
    private byte[] publicKey;

    /**
     * 私钥
     */
    private byte[] privateKey;

    /**
     * 初始化密钥
     * 
     * @throws Exception
     */
    @Before
    public void initKey() throws Exception {

        Map<String, Object> keyMap = RSACoder.initKey();

        publicKey = RSACoder.getPublicKey(keyMap);

        privateKey = RSACoder.getPrivateKey(keyMap);

        System.err.println("公钥: \n" + Base64.encodeBase64String(publicKey));
        System.err.println("私钥: \n" + Base64.encodeBase64String(privateKey));
    }

    /**
     * 校验
     * 
     * @throws Exception
     */
    @Test
    public void testSign() throws Exception {

        String inputStr = "RSA数字签名";
        byte[] data = inputStr.getBytes();

        // 产生签名
        byte[] sign = RSACoder.sign(data, privateKey);
        System.err.println("签名:\n" + Hex.encodeHexString(sign));

        // 验证签名
        boolean status = RSACoder.verify(data, publicKey, sign);
        System.err.println("状态:\n" + status);
        assertTrue(status);

    }
}

数字签名标准算法——DSA

在实现层面上,可以认为DSA算法实现就是RSA算法实现的精简版。DSA算法仅支持SHA系列消息摘要算法。

DSA算法

DSA数字签名示例:

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Before;
import org.junit.Test;

public class DSACoder {

    /**
     * 数字签名密钥算法
     */
    public static final String ALGORITHM = "DSA";

    /**
     * 数字签名
     * 签名/验证算法
     */
    public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";
    
    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "DSAPublicKey";

    /**
     * 私钥
     */
    private static final String PRIVATE_KEY = "DSAPrivateKey";
    
    /**
     * DSA密钥长度 
     * 默认1024位, 
     * 密钥长度必须是64的倍数, 
     * 范围在512至1024位之间(含)
     */
    private static final int KEY_SIZE = 1024;
    
    /**
     * 签名
     * 
     * @param data
     *            待签名数据
     * @param privateKey
     *            私钥
     * @return byte[] 数字签名
     * @throws Exception
     */
    public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {

        // 还原私钥
        // 转换私钥材料
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);

        // 实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

        // 生成私钥对象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 初始化Signature
        signature.initSign(priKey);

        // 更新
        signature.update(data);

        // 签名
        return signature.sign();
    }

    /**
     * 校验
     * 
     * @param data
     *            待校验数据
     * @param publicKey
     *            公钥
     * @param sign
     *            数字签名
     * 
     * @return boolean 校验成功返回true 失败返回false
     * @throws Exception
     * 
     */
    public static boolean verify(byte[] data, byte[] publicKey, byte[] sign)
            throws Exception {

        // 还原公钥
        // 转换公钥材料
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);

        // 实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

        // 取公钥匙对象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);

        // 实例话Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 初始化Signature
        signature.initVerify(pubKey);

        // 更新
        signature.update(data);

        // 验证
        return signature.verify(sign);
    }

    /**
     * 生成密钥
     * 
     * @return 密钥对象
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {

        // 初始化密钥对儿生成器
        KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);

        // 实例化密钥对儿生成器
        keygen.initialize(KEY_SIZE, new SecureRandom());

        // 实例化密钥对儿
        KeyPair keys = keygen.genKeyPair();

        DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();

        DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();

        // 封装密钥
        Map<String, Object> map = new HashMap<String, Object>(2);

        map.put(PUBLIC_KEY, publicKey);
        map.put(PRIVATE_KEY, privateKey);

        return map;
    }

    /**
     * 取得私钥
     * 
     * @param keyMap
     *            密钥Map
     * @return byte[] 私钥
     * @throws Exception
     */
    public static byte[] getPrivateKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PRIVATE_KEY);

        return key.getEncoded();
    }

    /**
     * 取得公钥
     * 
     * @param keyMap
     *            密钥Map
     * @return byte[] 公钥
     * @throws Exception
     */
    public static byte[] getPublicKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PUBLIC_KEY);

        return key.getEncoded();
    }

    /**
     * 公钥
     */
    private byte[] publicKey;

    /**
     * 私钥
     */
    private byte[] privateKey;

    /**
     * 初始化密钥
     * 
     * @throws Exception
     */
    @Before
    public void initKey() throws Exception {

        Map<String, Object> keyMap = DSACoder.initKey();

        publicKey = DSACoder.getPublicKey(keyMap);

        privateKey = DSACoder.getPrivateKey(keyMap);

        System.err.println("公钥: \n" + Base64.encodeBase64String(publicKey));
        System.err.println("私钥: \n" + Base64.encodeBase64String(privateKey));
    }

    /**
     * 校验签名
     * 
     * @throws Exception
     */
    @Test
    public void test() throws Exception {

        String inputStr = "DSA数字签名";
        byte[] data = inputStr.getBytes();

        // 产生签名
        byte[] sign = DSACoder.sign(data, privateKey);
        System.err.println("签名:\r" + Hex.encodeHexString(sign));

        // 验证签名
        boolean status = DSACoder.verify(data, publicKey, sign);
        System.err.println("状态:\r" + status);
        assertTrue(status);

    }

}

椭圆曲线数字签名算法——ECDSA

ECDSA算法相对于传统签名算法具有速度快、强度高、签名短等优点。微软操作系统及办公软件的序列号验证就使用了该算法。

算法 密钥长度 默认长度 签名长度 实现的方
NONEwithECDSA 112-571 256 128 JDK/BC
RIPEMD160withECDSA 同上 256 160 BC
SHA1withECDSA ... 256 160 JDK/BC
SHA224withECDSA ... 256 224 BC
SHA256withECDSA ... 256 256 JDK/BC
SHA384withECDSA ... 256 384 JDK/BC
SHA512withECDSA ... 256 512 JDK/BC

ECDSA算法示例:


import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Before;
import org.junit.Test;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class ECDSACoder {

    /**
     * 数字签名 密钥算法
     */
    private static final String KEY_ALGORITHM = "ECDSA";

    /**
     * 数字签名 签名/验证算法
     * 
     * Bouncy Castle支持以下7种算法
     * NONEwithECDSA 
     * RIPEMD160withECDSA 
     * SHA1withECDSA
     * SHA224withECDSA 
     * SHA256withECDSA 
     * SHA384withECDSA 
     * SHA512withECDSA
     */
    private static final String SIGNATURE_ALGORITHM = "SHA512withECDSA";

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "ECDSAPublicKey";

    /**
     * 私钥
     */
    private static final String PRIVATE_KEY = "ECDSAPrivateKey";

    /**
     * 初始化密钥
     * 
     * @return Map 密钥Map
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {

        // 加入BouncyCastleProvider支持
        Security.addProvider(new BouncyCastleProvider());

        BigInteger p = new BigInteger(
                "883423532389192164791648750360308885314476597252960362792450860609699839");
 
        ECFieldFp ecFieldFp = new ECFieldFp(p);

        BigInteger a = new BigInteger(
                "7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc",
                16);
 
        BigInteger b = new BigInteger(
                "6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a",
                16);
 
        EllipticCurve ellipticCurve = new EllipticCurve(ecFieldFp, a, b);

        BigInteger x = new BigInteger(
                "110282003749548856476348533541186204577905061504881242240149511594420911");
 
        BigInteger y = new BigInteger(
                "869078407435509378747351873793058868500210384946040694651368759217025454");
 
        ECPoint g = new ECPoint(x, y);

        BigInteger n = new BigInteger(
                "883423532389192164791648750360308884807550341691627752275345424702807307");

        ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g,
                n, 1);

        // 实例化密钥对儿生成器
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);

        // 初始化密钥对儿生成器
        kpg.initialize(ecParameterSpec, new SecureRandom());

        // 生成密钥对儿
        KeyPair keypair = kpg.generateKeyPair();

        ECPublicKey publicKey = (ECPublicKey) keypair.getPublic();

        ECPrivateKey privateKey = (ECPrivateKey) keypair.getPrivate();

        // 封装密钥
        Map<String, Object> map = new HashMap<String, Object>(2);

        map.put(PUBLIC_KEY, publicKey);
        map.put(PRIVATE_KEY, privateKey);

        return map;
    }

    /**
     * 取得私钥
     * 
     * @param keyMap
     *            密钥Map
     * @return byte[] 私钥
     * @throws Exception
     */
    public static byte[] getPrivateKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PRIVATE_KEY);

        return key.getEncoded();
    }

    /**
     * 取得公钥
     * 
     * @param keyMap
     *            密钥Map
     * @return byte[] 公钥
     * @throws Exception
     */
    public static byte[] getPublicKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PUBLIC_KEY);

        return key.getEncoded();
    }

    /**
     * 签名
     * 
     * @param data
     *            待签名数据
     * @param privateKey
     *            私钥
     * @return byte[] 数字签名
     * @throws Exception
     */
    public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {

        // 加入BouncyCastleProvider支持
        Security.addProvider(new BouncyCastleProvider());

        // 转换私钥材料
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);

        // 实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        // 取私钥匙对象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 初始化Signature
        signature.initSign(priKey);

        // 更新
        signature.update(data);

        // 签名
        return signature.sign();
    }

    /**
     * 校验
     * 
     * @param data
     *            待校验数据
     * @param publicKey
     *            公钥
     * @param sign
     *            数字签名
     * @return boolean 校验成功返回true 失败返回false
     * @throws Exception
     * 
     */
    public static boolean verify(byte[] data, byte[] publicKey, byte[] sign)
            throws Exception {

        // 加入BouncyCastleProvider支持
        Security.addProvider(new BouncyCastleProvider());

        // 转换公钥材料
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);

        // 实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        // 生成公钥
        PublicKey pubKey = keyFactory.generatePublic(keySpec);

        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 初始化Signature
        signature.initVerify(pubKey);

        // 更新
        signature.update(data);

        // 验证
        return signature.verify(sign);
    }

    /**
     * 公钥
     */
    private byte[] publicKey;

    /**
     * 私钥
     */
    private byte[] privateKey;

    /**
     * 初始化密钥
     * 
     * @throws Exception
     */
    @Before
    public void initKey() throws Exception {

        Map<String, Object> keyMap = ECDSACoder.initKey();

        publicKey = ECDSACoder.getPublicKey(keyMap);

        privateKey = ECDSACoder.getPrivateKey(keyMap);

        System.err.println("公钥: \n" + Base64.encodeBase64String(publicKey));
        System.err.println("私钥: \n" + Base64.encodeBase64String(privateKey));
    }

    /**
     * 校验
     * 
     * @throws Exception
     */
    @Test
    public void test() throws Exception {

        String inputStr = "ECDSA 数字签名";
        byte[] data = inputStr.getBytes();

        // 产生签名
        byte[] sign = ECDSACoder.sign(data, privateKey);
        System.err.println("签名:\r" + Hex.encodeHexString(sign));

        // 验证签名
        boolean status = ECDSACoder.verify(data, publicKey, sign);
        System.err.println("状态:\r" + status);
        assertTrue(status);

    }

}
发布了205 篇原创文章 · 获赞 217 · 访问量 235万+

猜你喜欢

转载自blog.csdn.net/heng615975867/article/details/105359893