数字签名详解与例子

    xiaoxiao2022-06-23  33

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

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

    数字签名的产生过程如图如示:

    数字签名的校验过程:

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

    package com.eudi.encode; import java.security.MessageDigest; import java.security.PrivateKey; import java.security.PublicKey; import javax.crypto.Cipher; import sun.misc.BASE64Encoder; /** * 用数字签名来保证传输安全的例子 * */ public class MD5withRSA { public static void main( String[] args ) throws Exception { //要传送的明文 String content = "我是中国人,我爱自己的祖国"; //生成公钥私钥,用于生成数字签名 RSA rsa = new RSA(); PublicKey publicKey = rsa.getPublicKey(); PrivateKey privateKey = rsa.getPrivateKey(); //生成数字签名 byte[] mysign = sign(content.getBytes(), privateKey); //验证数字签名 boolean result = verify(content.getBytes(), mysign, publicKey); if(result) { System.out.println("验证成功,数据没有被修改!"); }else { System.out.println("验证失败,数据被修改过!"); } } /** * @param conentBytes * @param mysign * @param publicKey * @return * @throws Exception */ private static boolean verify(byte[] contentBytes, byte[] mysign, PublicKey publicKey) throws Exception { //首先对签名进行解密 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("验证成功,传送的内容为:" + new String(contentBytes)); return true; } else { return false; } } /** * 采用MD5withRSA算法对bytes进行签名 * @param bytes * @param privateKey * @return * @throws Exception */ private static byte[] sign(byte[] conentBytes, PrivateKey privateKey) throws Exception { //生成数字摘要 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); //这就是最后生成的签名 return encrytBytes; } /** * 用base64编码byte数组 * @param bytes * @return */ private static String encryptBASE64(byte[] bytes) { BASE64Encoder encoder = new BASE64Encoder(); return encoder.encodeBuffer(bytes); } } package com.eudi.encode; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; public class RSA { private KeyPair keyPair; /** * 构造RSA对象,初始化keyPair * @throws Exception */ public RSA() throws Exception{ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(1024); keyPair = keyPairGenerator.generateKeyPair(); } /** * @return */ public PublicKey getPublicKey() { return keyPair.getPublic(); } /** * @return */ public PrivateKey getPrivateKey() { return keyPair.getPrivate(); } }

    运行结果

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

    以下是SHAwithRSA签名例子

    package com.eudi.encode; import java.security.MessageDigest; import java.security.PrivateKey; import java.security.PublicKey; import javax.crypto.Cipher; import sun.misc.BASE64Encoder; /** * 用数字签名来保证传输安全的例子 * */ public class SHA1withRSA { public static void main( String[] args ) throws Exception { //要传送的明文 String content = "我是中国人,我爱自己的祖国"; //生成公钥私钥,用于生成数字签名 RSA rsa = new RSA(); PublicKey publicKey = rsa.getPublicKey(); PrivateKey privateKey = rsa.getPrivateKey(); //生成数字签名 byte[] mysign = sign(content.getBytes(), privateKey); //验证数字签名 boolean result = verify(content.getBytes(), mysign, publicKey); if(result) { System.out.println("验证成功,数据没有被修改!"); }else { System.out.println("验证失败,数据被修改过!"); } } /** * @param conentBytes * @param mysign * @param publicKey * @return * @throws Exception */ private static boolean verify(byte[] contentBytes, byte[] mysign, PublicKey publicKey) throws Exception { //首先对签名进行解密 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("验证成功,传送的内容为:" + new String(contentBytes)); return true; } else { return false; } } /** * 采用SHA1withRSA算法对bytes进行签名 * @param bytes * @param privateKey * @return * @throws Exception */ private static byte[] sign(byte[] conentBytes, PrivateKey privateKey) throws Exception { //生成数字摘要 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); //这就是最后生成的签名 return encrytBytes; } /** * 用base64编码byte数组 * @param bytes * @return */ private static String encryptBASE64(byte[] bytes) { BASE64Encoder encoder = new BASE64Encoder(); return encoder.encodeBuffer(bytes); } }

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

    以下是笔者的代码实现

    package com.eudi.encode; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; /** * 用数字签名来保证传输安全的例子 * */ public class SignatureAPI { public static void main( String[] args ) throws Exception { //要传送的明文 String content = "我是中国人,我爱自己的祖国"; //生成公钥私钥,用于生成数字签名 RSA rsa = new RSA(); PublicKey publicKey = rsa.getPublicKey(); PrivateKey privateKey = rsa.getPrivateKey(); //生成数字签名 byte[] mysign = sign(content.getBytes(), privateKey); //验证数字签名 boolean result = 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 */ private static boolean verify(byte[] contentBytes, byte[] mysign, PublicKey publicKey) throws Exception { Signature signature = Signature.getInstance("MD5withRSA"); signature.initVerify(publicKey); signature.update(contentBytes); return signature.verify(mysign); } /** * 采用Java的Signature API进行签名 * @param bytes * @param privateKey * @return * @throws Exception */ private static byte[] sign(byte[] conentBytes, PrivateKey privateKey) throws Exception { Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(privateKey); signature.update(conentBytes); return signature.sign(); } }

    转载请注明原文地址: https://ju.6miu.com/read-1123215.html

    最新回复(0)