数字签名详解与例子
2017-02-05 15:17
302 查看
签名认证是对非对称加密技术与数字摘要技术的综合运用,指的是将通信内容的摘要信息使用发送者的私钥进行加密,然后将密文与原文一起传输给信息的接收者,接收者通过发送者的公钥信息来解密被加密的摘要作息,然后使用与发送者相同的摘要算法,对接收到的内容采用相同的方式方式产生摘要串,与解密的摘要串进行对比,如果相同,则说明接收到的内容是完整的,在传输过程中没有受到第三方的篡改,否则说明通信内容已被第三方修改。
我们知道,每个人都有其特有的私钥,且都是对外界保密的,而通过私钥加密的信息,只能通过其对应的公钥来进行解密。因此,私钥可以代表私钥持有者的身份,可以通过私钥对应的公钥来对私钥拥有者的身份进行校验。通过数字签名,能够确认消息是消息发送方签名并发送过来的,因为其他人根本假冒不了消息发送方的签名,他们没有消息发送者的私钥。而不同的内容,摘要信息千差万别,通过数字摘要算法,可以确保传输内容的完整性,如果传输内容在中途被篡改了,对应的数字签名的值也将发生改变。
数字签名的产生过程如图如示:
数字签名的校验过程:
区别于不同的摘要算法,不同的非对称加密方式,数字签名的算法也不尽相同。以下为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; } }
相关文章推荐
- DOM、BOM操作集合-DOM基本操作思维导图与window对象思维导图
- USACO1.3 修理牛棚 Barn Repair
- Create Phone Number
- 读读MaNGOS系列之总览
- 立Flag!!
- MFC:调用Invalidate()的作用
- c#连接oracle配置文件
- css溢出加滚动条
- C#微信开发之获取接口调用凭据
- 定义宏
- SQL查询语句关键字方法
- 先锋机器人学习笔记_1-3 2d 建模和 Mapper 地图编辑器
- CENTOS: Docker的Tomcat容器里的reboot
- spring maven 搭建dubbo框架 服务端
- [Android开发] EventBus3 使用方法教程总结
- Android Studio Gradle打包实践之多渠道+版本号管理
- 3 Kafka两个版本在Zookeeper的元数据存储
- 常用SQL查询语句
- c#画平行线和垂线的代码
- iOS 真机证书描述文件配置逻辑