数字签名详解与例子









签名认证是对非对称加密技术与数字摘要技术的综合运用,指的是将通信内容的摘要信息使用发送者的私钥进行加密,然后将密文与原文一起传输给信息的接收者,接收者通过发送者的公钥信息来解密被加密的摘要作息,然后使用与发送者相同的摘要算法,对接收到的内容采用相同的方式方式产生摘要串,与解密的摘要串进行对比,如果相同,则说明接收到的内容是完整的,在传输过程中没有受到第三方的篡改,否则说明通信内容已被第三方修改。

我们知道,每个人都有其特有的私钥,且都是对外界保密的,而通过私钥加密的信息,只能通过其对应的公钥来进行解密。因此,私钥可以代表私钥持有者的身份,可以通过私钥对应的公钥来对私钥拥有者的身份进行校验。通过数字签名,能够确认消息是消息发送方签名并发送过来的,因为其他人根本假冒不了消息发送方的签名,他们没有消息发送者的私钥。而不同的内容,摘要信息千差万别,通过数字摘要算法,可以确保传输内容的完整性,如果传输内容在中途被篡改了,对应的数字签名的值也将发生改变。

数字签名的产生过程如图如示:
数字签名详解与例子

数字签名的校验过程:

数字签名详解与例子

区别于不同的摘要算法,不同的非对称加密方式,数字签名的算法也不尽相同。以下为MD5withRSA和SHA1withRSA的例子。

packagecom.eudi.encode;
 
importjava.security.MessageDigest;
importjava.security.PrivateKey;
importjava.security.PublicKey;
 
importjavax.crypto.Cipher;
 
importsun.misc.BASE64Encoder;
 
/**
 * 用数字签名来保证传输安全的例子
 *
 */
publicclass MD5withRSA 
{
    publicstatic void main( String[] args ) throwsException
    {
        //要传送的明文
        String content = "我是中国人,我爱自己的祖国";
         
        //生成公钥私钥,用于生成数字签名
        RSA rsa = newRSA();
        PublicKey publicKey = rsa.getPublicKey();
        PrivateKey privateKey = rsa.getPrivateKey();
        //生成数字签名
        byte[] mysign = sign(content.getBytes(), privateKey);
         
        //验证数字签名
        booleanresult = verify(content.getBytes(), mysign, publicKey);
        if(result) {
            System.out.println("验证成功,数据没有被修改!");
        }else{
            System.out.println("验证失败,数据被修改过!");
        }
    }
 
    /**
     * @param conentBytes
     * @param mysign
     * @param publicKey
     * @return
     * @throws Exception
     */
    privatestatic boolean verify(byte[] contentBytes, byte[] mysign,
            PublicKey publicKey) throwsException {
        //首先对签名进行解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] decrytBytes = cipher.doFinal(mysign);    //这是解密开的数字摘要
         
        //接下来对明文进行摘要
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] srcBytes = md.digest(contentBytes);  //这是原数字摘要
         
        //比较两个摘要是否相同
        //将字节数据编码成Base64再进行字符串比较
        if(encryptBASE64(decrytBytes).equals(encryptBASE64(srcBytes))) {
            System.out.println("验证成功,传送的内容为:"+ newString(contentBytes));
            returntrue;
        }else{
            returnfalse;
        }
    }
 
    /**
     * 采用MD5withRSA算法对bytes进行签名
     * @param bytes
     * @param privateKey
     * @return
     * @throws Exception 
     */
    privatestatic byte[] sign(byte[] conentBytes, PrivateKey privateKey) throwsException {
        //生成数字摘要
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] md5Bytes = md.digest(conentBytes);
         
        //对摘要进行私钥加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encrytBytes = cipher.doFinal(md5Bytes);
        //这就是最后生成的签名
        returnencrytBytes;
    }
     
    /**
     * 用base64编码byte数组 
     * @param bytes 
     * @return 
     */ 
    privatestatic String encryptBASE64(byte[] bytes) {  
        BASE64Encoder encoder = newBASE64Encoder();  
        returnencoder.encodeBuffer(bytes);  
    } 
}



packagecom.eudi.encode;
 
importjava.security.KeyPair;
importjava.security.KeyPairGenerator;
importjava.security.PrivateKey;
importjava.security.PublicKey;
 
publicclass RSA {
 
    privateKeyPair keyPair;
     
    /**
     * 构造RSA对象,初始化keyPair
     * @throws Exception
     */
    publicRSA() throwsException{
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        keyPair = keyPairGenerator.generateKeyPair();
    }
     
    /**
     * @return
     */
    publicPublicKey getPublicKey() {
        returnkeyPair.getPublic();
    }
 
    /**
     * @return
     */
    publicPrivateKey getPrivateKey() {
        returnkeyPair.getPrivate();
    }
}


运行结果

验证成功,传送的内容为:我是中国人,我爱自己的祖国
验证成功,数据没有被修改!



以下是SHAwithRSA签名例子

packagecom.eudi.encode;
 
importjava.security.MessageDigest;
importjava.security.PrivateKey;
importjava.security.PublicKey;
 
importjavax.crypto.Cipher;
 
importsun.misc.BASE64Encoder;
 
/**
 * 用数字签名来保证传输安全的例子
 *
 */
publicclass SHA1withRSA 
{
    publicstatic void main( String[] args ) throwsException
    {
        //要传送的明文
        String content = "我是中国人,我爱自己的祖国";
         
        //生成公钥私钥,用于生成数字签名
        RSA rsa = newRSA();
        PublicKey publicKey = rsa.getPublicKey();
        PrivateKey privateKey = rsa.getPrivateKey();
        //生成数字签名
        byte[] mysign = sign(content.getBytes(), privateKey);
         
        //验证数字签名
        booleanresult = verify(content.getBytes(), mysign, publicKey);
        if(result) {
            System.out.println("验证成功,数据没有被修改!");
        }else{
            System.out.println("验证失败,数据被修改过!");
        }
    }
 
    /**
     * @param conentBytes
     * @param mysign
     * @param publicKey
     * @return
     * @throws Exception
     */
    privatestatic boolean verify(byte[] contentBytes, byte[] mysign,
            PublicKey publicKey) throwsException {
        //首先对签名进行解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] decrytBytes = cipher.doFinal(mysign);    //这是解密开的数字摘要
         
        //接下来对明文进行摘要
        MessageDigest md = MessageDigest.getInstance("SHA1");
        byte[] srcBytes = md.digest(contentBytes);  //这是原数字摘要
         
        //比较两个摘要是否相同
        //将字节数据编码成Base64再进行字符串比较
        if(encryptBASE64(decrytBytes).equals(encryptBASE64(srcBytes))) {
            System.out.println("验证成功,传送的内容为:"+ newString(contentBytes));
            returntrue;
        }else{
            returnfalse;
        }
    }
 
    /**
     * 采用SHA1withRSA算法对bytes进行签名
     * @param bytes
     * @param privateKey
     * @return
     * @throws Exception 
     */
    privatestatic byte[] sign(byte[] conentBytes, PrivateKey privateKey) throwsException {
        //生成数字摘要
        MessageDigest md = MessageDigest.getInstance("SHA1");
        byte[] rha1Bytes = md.digest(conentBytes);
         
        //对摘要进行私钥加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encrytBytes = cipher.doFinal(rha1Bytes);
        //这就是最后生成的签名
        returnencrytBytes;
    }
     
    /**
     * 用base64编码byte数组 
     * @param bytes 
     * @return 
     */ 
    privatestatic String encryptBASE64(byte[] bytes) {  
        BASE64Encoder encoder = newBASE64Encoder();  
        returnencoder.encodeBuffer(bytes);  
    } 
}



Java提供了比较友好的API来使用数字签名,在sign方法中,先获取MD5withRSA的一个实例,然后使用私钥对signature进行初始化,调用update传入签名内容,最后生成签名。在verify方法中,使用公钥来对signature进行初始化,调用update传入需要校验的内容,调用signature的verify方法校验签名,并返回结果。

以下是笔者的代码实现

 

packagecom.eudi.encode;
 
importjava.security.PrivateKey;
importjava.security.PublicKey;
importjava.security.Signature;
 
/**
 * 用数字签名来保证传输安全的例子
 *
 */
publicclass SignatureAPI 
{
    publicstatic void main( String[] args ) throwsException
    {
        //要传送的明文
        String content = "我是中国人,我爱自己的祖国";
         
        //生成公钥私钥,用于生成数字签名
        RSA rsa = newRSA();
        PublicKey publicKey = rsa.getPublicKey();
        PrivateKey privateKey = rsa.getPrivateKey();
        //生成数字签名
        byte[] mysign = sign(content.getBytes(), privateKey);
         
        //验证数字签名
        booleanresult = verify(content.getBytes(), mysign, publicKey);
        if(result) {
            System.out.println("验证成功,数据没有被修改!");
        }else{
            System.out.println("验证失败,数据被修改过!");
        }
    }
 
    /**
     * 基本Java的signature API对数字签名进行校验
     * @param conentBytes
     * @param mysign
     * @param publicKey
     * @return
     * @throws Exception
     */
    privatestatic boolean verify(byte[] contentBytes, byte[] mysign,
            PublicKey publicKey) throwsException {
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(publicKey);
        signature.update(contentBytes);
        returnsignature.verify(mysign);
    }
 
    /**
     * 采用Java的Signature API进行签名
     * @param bytes
     * @param privateKey
     * @return
     * @throws Exception 
     */
    privatestatic byte[] sign(byte[] conentBytes, PrivateKey privateKey) throwsException {
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(privateKey);
        signature.update(conentBytes);
        returnsignature.sign();
    }
}



还有另一种利用其他加密类进行的数字签名 我以为一并展示出来

public class DigitalSignatureTest2 {


    public static void main(String args[]) throws Exception {

        String content = "我是中国人,我爱自己的祖国";
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();

        byte[] mysign=sign(content.getBytes(),rsaPrivateKey);

        boolean result = verify(content.getBytes(), mysign, rsaPublicKey);
        if(result) {
            System.out.println("验证成功,数据没有被修改!");
        }else {
            System.out.println("验证失败,数据被修改过!");
        }

    }


    /**
     * 执行签名(私钥签名)
     * 采用MD5withRSA算法对bytes进行签名
     * @param conentBytes
     * @param rsaPrivateKey
     * @return
     * @throws Exception
     */
    private static byte[] sign(byte[] conentBytes, RSAPrivateKey rsaPrivateKey) throws Exception {
        //生成数字摘要
        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(conentBytes);
        byte[] result=signature.sign();
        System.out.println("私钥加密的数字签名"+ Hex.encodeHexString(result));
        return result;
    }


    /**
     * 验证签名(公钥验证)
     * @param contentBytes
     * @param mysign
     * @param rsaPublicKey
     * @return
     * @throws Exception
     */
    private static boolean verify(byte[] contentBytes, byte[] mysign,
                                  RSAPublicKey rsaPublicKey) throws Exception {
        //首先对签名进行解密
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(publicKey);
        signature.update(contentBytes);
        boolean bool = signature.verify(mysign);
        return bool;
    }
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数字签名是一种用于验证和确保数字数据完整性、认证身份和防止篡改的技术。它基于公钥密码学的原理。数字签名使用私钥对数据进行加密生成签名,然后使用对应的公钥进行验证签名的有效性。 数字签名的过程包括以下步骤: 1. 生成密钥对:首先,需要生成一对密钥,包括一个私钥和一个公钥。私钥由签名者保留,公钥可以公开发布。 2. 数据签名:签名者使用私钥对要签名的数据进行加密生成签名。这个签名是唯一的,任何对数据进行篡改都会导致签名验证失败。 3. 签名验证:接收者使用签名者的公钥对接收到的数据和签名进行解密和验证。如果解密后的签名与解密后的数据匹配,说明数据完整且未被篡改。 数字证书是用于证明公钥的真实性和拥有者身份的电子文档。数字证书由权威机构(如数字证书颁发机构)颁发,其中包含公钥、证书持有者信息和颁发机构的数字签名。 数字证书的作用是确保公钥的合法性和信任,以及提供验证身份的方式。它可以用于安全通信、身份认证、数据完整性保护等多个领域。 在使用数字证书进行数字签名时,签名者的公钥会包含在数字证书中,接收者可以使用数字证书中的公钥对签名进行验证。数字证书的数字签名可以保证证书的真实性和可靠性。 总结来说,数字签名是一种通过加密和解密技术,用于验证数据完整性和身份认证的方法。而数字证书则是一种包含公钥、身份信息和数字签名的电子文档,用于证明公钥的真实性和拥有者身份。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值