java结合keytool如何实现非对称签名和验证详解
前言
本文主要介绍了关于java结合keytool实现非对称签名和验证的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧
参考 还有姊妹篇:java结合keytool实现非对称加密和解密
keytool的使用
keytool是JDK自带的一个密钥库管理工具。这里只用到了keytool的部分功能,包括生成密钥对,导出公钥等。keytool生成的公钥/私钥对存放到一个到了一个文件中,这个文件有密码保护,通称为keystore。
生成密钥对
$keytool-genkey-aliassignLegal-keystoreexamplestanstore-validity1800
生成别名为signLegal的密钥对,存放在密钥库examplestanstore中,证书的有效期是1800天(默认是90天)。
输入一系列的参数。输入的参数遵循了LDAP的风格和标准。可以想象,生成的密钥对可以看成LDAP的一个条目。
命令执行成功后会在当前目录下创建一个叫examplestanstore的文件。
查看密钥对
$keytool-list-keystoreexamplestanstore-v
列出了examplestanstore密钥库的中所有密钥对。-v参数表示详细信息,详细信息中有证书的失效时间。
导出公钥证书
$keytool-export-keystoreexamplestanstore-aliassignLegal-fileStanSmith.cer
导出的公钥存放在当前目录的StanSmith.cer文件中,是个二进制文件。
java签名和验证
参考了java安全官方教程。
在该官方教程中,GenSig.java类生成密钥对,对输入的文件进行签名,输出了一个签名结果文件sig和公钥suepk。
VerSig.java类接受三个参数:公钥文件名(suepk)、签名文件(sig)、被签名的源文件名(hello.txt)。
该教程解释了两个类的原理,并附加有源码。将源码下载并编译。创建一个hello.txt的文件作为被签名的目标文件,里面随便放点字符串。然后执行:
$javaGenSighello.txt(生成文件sig和suepk) $javaVerSigsuepksighello.txt signatureverifies: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
importjava.io.*; importjava.security.*; classGenSig2{ publicstaticvoidmain(String[]args){ if(args.length!=1){ System.out.println("Usage:javaGenSig2"); } elsetry{ /*createkeypaireusekeytool: $keytool-genkey-aliassignLegal-keystoreexamplestanstore-validity1800*/ //readkeystorefile KeyStoreks=KeyStore.getInstance("JKS"); FileInputStreamksfis=newFileInputStream("examplestanstore"); BufferedInputStreamksbufin=newBufferedInputStream(ksfis); //openkeystoreandgetprivatekey //aliasis'signLeal',kpasswd/spasswdis'vagrant' ks.load(ksbufin,"vagrant".toCharArray()); PrivateKeypriv=(PrivateKey)ks.getKey("signLegal","vagrant".toCharArray()); /*CreateaSignatureobjectandinitializeitwiththeprivatekey*/ Signaturedsa=Signature.getInstance("SHA1withDSA","SUN"); dsa.initSign(priv); /*Updateandsignthedata*/ FileInputStreamfis=newFileInputStream(args[0]); BufferedInputStreambufin=newBufferedInputStream(fis); byte[]buffer=newbyte[1024]; intlen; while(bufin.available()!=0){ len=bufin.read(buffer); dsa.update(buffer,0,len); }; bufin.close(); /*Nowthatallthedatatobesignedhasbeenreadin, generateasignatureforit*/ byte[]realSig=dsa.sign(); /*Savethesignatureinafile*/ FileOutputStreamsigfos=newFileOutputStream("sig"); sigfos.write(realSig); sigfos.close(); /*publickeyfilecanexportfromkeystoreusekeytool: $keytool-export-keystoreexamplestanstore-aliassignLegal-fileStanSmith.cer*/ }catch(Exceptione){ System.err.println("Caughtexception"+e.toString()); } };
编译后,这样运行:
$javaGenSig2hello.txt
会生成签名文件sig。
VerSig2.java
importjava.io.*; importjava.security.*; importjava.security.spec.*; classVerSig2{ publicstaticvoidmain(String[]args){ /*VerifyaDSAsignature*/ if(args.length!=3){ System.out.println("Usage:VerSigpublickeyfilesignaturefiledatafile"); } elsetry{ /*importencodedpubliccert*/ FileInputStreamcertfis=newFileInputStream(args[0]); java.security.cert.CertificateFactorycf= java.security.cert.CertificateFactory.getInstance("X.509"); java.security.cert.Certificatecert=cf.generateCertificate(certfis); PublicKeypubKey=cert.getPublicKey(); /*inputthesignaturebytes*/ FileInputStreamsigfis=newFileInputStream(args[1]); byte[]sigToVerify=newbyte[sigfis.available()]; sigfis.read(sigToVerify); sigfis.close(); /*createaSignatureobjectandinitializeitwiththepublickey*/ Signaturesig=Signature.getInstance("SHA1withDSA","SUN"); sig.initVerify(pubKey); /*Updateandverifythedata*/ FileInputStreamdatafis=newFileInputStream(args[2]); BufferedInputStreambufin=newBufferedInputStream(datafis); byte[]buffer=newbyte[1024]; intlen; while(bufin.available()!=0){ len=bufin.read(buffer); sig.update(buffer,0,len); }; bufin.close(); booleanverifies=sig.verify(sigToVerify); System.out.println("signatureverifies:"+verifies); }catch(Exceptione){ System.err.println("Caughtexception"+e.toString()); }; } }
编译后,这样运行(StanSmith.cer是利用keytool导出的公钥证书,见前文):
$javaVerSig2StanSmith.cersighello.txt signatureverifies:true
openssl
虽然也研究了一下openssl,但发现与java难以结合,难度也很大。例如它的教程中采用的是RSA,而上面的java使用的是DSA。所以只是贴在这里备忘,可以忽略。
参考
生成私钥
$opensslgenrsa-outkey.pem1024 $catkey.pem -----BEGINRSAPRIVATEKEY----- 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 -----ENDRSAPRIVATEKEY-----
导出公钥
$opensslrsa-inkey.pem-pubout-outpub-key.pem $catpub-key.pem -----BEGINPUBLICKEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzVDmu6Cf2QF7cERCGYU3B8Epm 6pkkpMZFgotphXMgAmBBNJbhSi7qPH4R5JlEm1ZXPr5DZH/pyJBWQhiiHGeUAOve +GOgvt9Rk25r7OEWYvn/GCr/JBfLBGqwtlzn/t2s2x04IooshsGkOd6YpZoztkED tu2gKHedFczF607IvwIDAQAB -----ENDPUBLICKEY-----
摘要计算
创建一个内容是1234的文本文件hello.txt。用openssl计算它的SHA256摘要(SHA256是jarsigner的默认摘要算法):
$cathello.txt 1234 $openssldgst-SHA256-outhello.sha256hello.txt $cathello.sha256 SHA256(hello.txt)=a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4
签名和验证
对摘要文件hello.sha256进行签名:
$opensslrsautl-sign-inhello.sha256-outhello.sign-inkeykey.pem
用公钥对签名进行验证:
$opensslrsautl-verify-inhello.sign-inkeypub-key.pem-pubin SHA256(hello.txt)=a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4
用公钥验证必须加上-pubin参数。用私钥对签名进行验证:
$opensslrsautl-verify-inhello.sign-inkeykey.pem SHA256(hello.txt)=a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4
验证的STD输出与摘要文件hello.sha256的内容一样,说明验证可以通过。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。