美文网首页java高级开发
Java之keytool非对称签名与验证

Java之keytool非对称签名与验证

作者: 老鼠AI大米_Java全栈 | 来源:发表于2018-10-01 12:00 被阅读6次

    本文参考

    什么是keytool?
    keytool是JDK自带的一个密钥库管理工具。这里只用到了keytool的部分功能,包括生成密钥对,导出公钥等。keytool生成的公钥/私钥对存放到一个到了一个文件中,这个文件有密码保护,通称为keystore。

    生成密钥对

    $ keytool -genkeypair \
    -alias config-server \
    -keystore config-server.keystore \
    -validity 1800 \
    -keyalg RSA \
    -dname "CN=yxx, OU=company, O=org, L=city, ST=province, C=china" \
    -keypass 222222 \
    -storepass 111111
    

    上面是创建密钥对的命令,具体参数如下:
    *alias 别名
    *keystore 密钥库名称
    *validity 有效时间
    *keyalg 密钥对方式
    *dname 基本信息
    *keypass 密钥口令
    *storepass 密钥库口令
    执行上面的命令,会有一个提示,如下:

    JKS 密钥库使用专用格式。
    建议使用 "keytool -importkeystore -srckeystore config-server.keystore 
    -destkeystore config-server.keystore -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。
    

    可以根据自己的情况看是否需要转P12。

    查看密钥对

    $ keytool -list -keystore examplestanstore -v
    

    列出了config-server.keystore密钥库的中所有密钥对。-v参数表示详细信息,详细信息中有证书的失效时间。

    导出公钥证书

    $ keytool -export -keystore config-server.keystore \
    -alias config-server -file config-server.cer
    

    导出的公钥存放在当前目录的StanSmith.cer文件中,是个二进制文件。

    java签名和验证
    参考了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中导出的是证书而不是公钥,两者的封装格式估计有差异。

    具体步骤
    1.利用keytool -genkey生成密钥对保存在keystore中(库文件是config-server.keystore)
    2.利用`keytool -export’从keystore中导出公钥证书(StanSmith.cer)
    3.利用新类GenSig2.java生成签名(文件名是sig),GenSig2.java会从keystore中取私钥
    4.将公钥(StanSmith.cer)、签名(sig)、被签名文件(hello.txt)发给验证方
    5.验证方利用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("config-server.keystore");
                    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
    

    学习交流,请加群:64691032

    相关文章

      网友评论

        本文标题:Java之keytool非对称签名与验证

        本文链接:https://www.haomeiwen.com/subject/ezxnoftx.html