java结合keytool如何实现非对称签名和验证详解
前言
本文主要介绍了关于java结合keytool实现非对称签名和验证的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧
还有姊妹篇:
keytool的使用
keytool是jdk自带的一个密钥库管理工具。这里只用到了keytool的部分功能,包括生成密钥对,导出公钥等。keytool生成的公钥/私钥对存放到一个到了一个文件中,这个文件有密码保护,通称为keystore。
生成密钥对
$ keytool -genkey -alias signlegal -keystore examplestanstore -validity 1800
生成别名为signlegal的密钥对,存放在密钥库examplestanstore中,证书的有效期是1800天(默认是90天)。
输入一系列的参数。输入的参数遵循了ldap的风格和标准。可以想象,生成的密钥对可以看成ldap的一个条目。
命令执行成功后会在当前目录下创建一个叫examplestanstore的文件。
查看密钥对
$ keytool -list -keystore examplestanstore -v
列出了examplestanstore密钥库的中所有密钥对。-v参数表示详细信息,详细信息中有证书的失效时间。
导出公钥证书
$ keytool -export -keystore examplestanstore -alias signlegal -file stansmith.cer
导出的公钥存放在当前目录的stansmith.cer文件中,是个二进制文件。
java签名和验证
参考了。
在该官方教程中,gensig.java类生成密钥对,对输入的文件进行签名,输出了一个签名结果文件sig和公钥suepk。
versig.java类接受三个参数:公钥文件名(suepk)、签名文件(sig)、被签名的源文件名(hello.txt)。
该教程解释了两个类的原理,并附加有源码。将源码下载并编译。创建一个hello.txt的文件作为被签名的目标文件,里面随便放点字符串。然后执行:
$ java gensig hello.txt (生成文件sig和suepk) $ java versig suepk sig hello.txt signature verifies: true
在实际使用时,密钥对不可能每次在程序中重新生成。而keytool恰好可以生成并相对安全保存密钥对。所以下面结合了keytool和java实现的功能。
结合keytool与java签名/验证
密钥对由keytool生成并保存到keystore中保护起来(keystore有密码)。公钥也从keystore中导出。gensig.java类只需要从keystore中取得私钥进行签名即可。
versig.java也要做适当的修改。貌似因为从keystore中导出的是证书而不是公钥,两者的封装格式估计有差异。
具体步骤
- 利用keytool -genkey生成密钥对保存在keystore中(库文件是examplestanstore)
- 利用`keytool -export'从keystore中导出公钥证书(stansmith.cer)
- 利用新类gensig2.java生成签名(文件名是sig),gensig2.java会从keystore中取私钥
- 将公钥(stansmith.cer)、签名(sig)、被签名文件(hello.txt)发给验证方
- 验证方利用versig2.java进行验证
下面是gensig2.java和versig2.java的源码和执行方式。
gensig2.java
import java.io.*; import java.security.*; class gensig2 { public static void main(string[] args) { if (args.length != 1) { system.out.println("usage: java gensig2 <nameoffiletosign>"); } else try{ /*create key paire use keytool: $ keytool -genkey -alias signlegal -keystore examplestanstore -validity 1800*/ // read keystore file keystore ks = keystore.getinstance("jks"); fileinputstream ksfis = new fileinputstream("examplestanstore"); bufferedinputstream ksbufin = new bufferedinputstream(ksfis); // open keystore and get private key // alias is 'signleal', kpasswd/spasswd is 'vagrant' ks.load(ksbufin, "vagrant".tochararray()); privatekey priv = (privatekey) ks.getkey("signlegal", "vagrant".tochararray()); /* create a signature object and initialize it with the private key */ signature dsa = signature.getinstance("sha1withdsa", "sun"); dsa.initsign(priv); /* update and sign the data */ fileinputstream fis = new fileinputstream(args[0]); bufferedinputstream bufin = new bufferedinputstream(fis); byte[] buffer = new byte[1024]; int len; while (bufin.available() != 0) { len = bufin.read(buffer); dsa.update(buffer, 0, len); }; bufin.close(); /* now that all the data to be signed has been read in, generate a signature for it */ byte[] realsig = dsa.sign(); /* save the signature in a file */ fileoutputstream sigfos = new fileoutputstream("sig"); sigfos.write(realsig); sigfos.close(); /* public key file can export from keystore use keytool: $ keytool -export -keystore examplestanstore -alias signlegal -file stansmith.cer */ } catch (exception e) { system.err.println("caught exception " + e.tostring()); } };
编译后,这样运行:
$ java gensig2 hello.txt
会生成签名文件sig。
versig2.java
import java.io.*; import java.security.*; import java.security.spec.*; class versig2 { public static void main(string[] args) { /* verify a dsa signature */ if (args.length != 3) { system.out.println("usage: versig publickeyfile signaturefile datafile"); } else try{ /* import encoded public cert */ fileinputstream certfis = new fileinputstream(args[0]); java.security.cert.certificatefactory cf = java.security.cert.certificatefactory.getinstance("x.509"); java.security.cert.certificate cert = cf.generatecertificate(certfis); publickey pubkey = cert.getpublickey(); /* input the signature bytes */ fileinputstream sigfis = new fileinputstream(args[1]); byte[] sigtoverify = new byte[sigfis.available()]; sigfis.read(sigtoverify ); sigfis.close(); /* create a signature object and initialize it with the public key */ signature sig = signature.getinstance("sha1withdsa", "sun"); sig.initverify(pubkey); /* update and verify the data */ fileinputstream datafis = new fileinputstream(args[2]); bufferedinputstream bufin = new bufferedinputstream(datafis); byte[] buffer = new byte[1024]; int len; while (bufin.available() != 0) { len = bufin.read(buffer); sig.update(buffer, 0, len); }; bufin.close(); boolean verifies = sig.verify(sigtoverify); system.out.println("signature verifies: " + verifies); } catch (exception e) { system.err.println("caught exception " + e.tostring()); }; } }
编译后,这样运行(stansmith.cer是利用keytool导出的公钥证书,见前文):
$ java versig2 stansmith.cer sig hello.txt signature verifies: true
openssl
虽然也研究了一下openssl,但发现与java难以结合,难度也很大。例如它的教程中采用的是rsa,而上面的java使用的是dsa。所以只是贴在这里备忘,可以忽略。
生成私钥
$ openssl genrsa -out key.pem 1024 $ cat key.pem -----begin rsa private key----- miicxqibaakbgqczvdmu6cf2qf7cercgyu3b8epm6pkkpmzfgotphxmgambbnjbh si7qph4r5jlem1zxpr5dzh/pyjbwqhiihgeuaove+gogvt9rk25r7oewyvn/gcr/ jbflbgqwtlzn/t2s2x04iooshsgkod6ypzoztkedtu2gkhedfczf607ivwidaqab aogamdbiqumwqyomuvctjqxixiwrwysvx09ci1liszl7kfw/ecazhq19whazgxmm 9zpmxratxluccvfkfa6mlfda+zoblksydoecwnb+tsaumf9xk8uhw/g8c+ykq9og g9uiy8rknl12zaiu9h8l82ud0cktfw2636/pukgtp+4ybxecqqdhkdh8lwgumg7h yiw5476qohnpl7c3ofpgtaozmzjkjmpfrzgr4b5pjcgnoldotlkatcbpmxtlwwjj szabdarjakeay+nwdozc1yqrtrkzqx1brnjo3iytfkl3t1xawyz5sy1ib7+4fsod eh3br5e1o5yripy2gjzvp2oaat3tz6is9qjasviywu+qo4hx3vk9847gwtrrjxfk 1jafhcedgujezf8ku08dvl/alvrcpxzzlzluenfmz5fwudkcq87dj7g2rqjbamdm +snipdmea8n0prvfjjld7pmp4pu6m3fzx3owiqj5t9tscjxzqbxcmdxizzs7dkll ta/6kek64pfvfa25tgucqqctm1vwfnkjfbd+0huf6was3odjuo0gkk/qijdn7m5/ i0kxeapkxtto3oiucqgeyl/sqy3wjm0476w48+xusqef -----end rsa private key-----
导出公钥
$ openssl rsa -in key.pem -pubout -out pub-key.pem $ cat pub-key.pem -----begin public key----- migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqczvdmu6cf2qf7cercgyu3b8epm 6pkkpmzfgotphxmgambbnjbhsi7qph4r5jlem1zxpr5dzh/pyjbwqhiihgeuaove +gogvt9rk25r7oewyvn/gcr/jbflbgqwtlzn/t2s2x04iooshsgkod6ypzoztked tu2gkhedfczf607ivwidaqab -----end public key-----
摘要计算
创建一个内容是1234的文本文件hello.txt。用openssl计算它的sha256摘要(sha256是jarsigner的默认摘要算法):
$ cat hello.txt 1234 $ openssl dgst -sha256 -out hello.sha256 hello.txt $ cat hello.sha256 sha256(hello.txt)= a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4
签名和验证
对摘要文件hello.sha256进行签名:
$ openssl rsautl -sign -in hello.sha256 -out hello.sign -inkey key.pem
用公钥对签名进行验证:
$ openssl rsautl -verify -in hello.sign -inkey pub-key.pem -pubin sha256(hello.txt)= a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4
用公钥验证必须加上-pubin参数。 用私钥对签名进行验证:
$ openssl rsautl -verify -in hello.sign -inkey key.pem sha256(hello.txt)= a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4
验证的std输出与摘要文件hello.sha256的内容一样,说明验证可以通过。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: Java 判断字符串a和b是否互为旋转词
下一篇: EEPROM IIC