使用openssl生成及验证Java签名内容

1.  前言

在与合作方进行通信时,为了保证数据完整性,通常会使用数字签名。

有时由于合作方的理解不一致,或使用方法不正确,可能导致合作方生成签名或验证签名时失败。

在遇到以上情况时,通常比较难快速定位问题,可以使用openssl对签名进行检查,确认导致验证签名失败的具体原因。

以下需要使用keytool与openssl命令,可查看https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html、https://www.openssl.org/docs/manmaster/man1/

以下假设应用程序使用Java开发,将密钥对内容保存keystore文件中,生成keystore文件时密钥算法为RSA,签名算法为SHA256withRSA。

keystore文件生成命令略。

2.  使用openssl验证Java签名结果

使用openssl命令可以验证Java生成的签名结果是否正确。

首先需要将keystore文件中的公钥导出,再使用openssl命令根据公钥对签名进行验证。

2.1  导出公钥

在将keystore文件中的公钥导出时,需要先从keystore文件导出X.509证书文件,再从X.509证书文件导出公钥。

l  导出X.509证书文件

使用Java的keytool命令从keystore文件导出X.509证书文件,命令如下。

当指定-rfc选项时,导出的证书文件为Base64格式;若不指定-rfc选项,导出的证书文件为二进制格式。

keystore=[需要导出的keystore文件名/完整路径]

alias=[keystore文件需要导出证书文件的别名]

storepass=[keystore文件的密码]

exportcert=[导出后的证书文件名/完整路径]

 

keytool -exportcert -alias $alias -keystore $keystore -file $exportcert -storepass $storepass -rfc

示例:

keystore=test.jks

alias=test_alias

storepass=test_pwd

exportcert=test.cer

 

keytool -exportcert -alias $alias -keystore $keystore -file $exportcert -storepass $storepass -rfc

导出成功时,提示为“Certificate stored in file <...>”。

导出后的证书文件内容如下所示:

-----BEGIN CERTIFICATE-----

MIIDCTCCAfGgAwIBAgIEDt12JDANBgkqhkiG9w0BAQsFADA1MQ8wDQYDVQQKDAZvX3Rlc3QxEDAO

...

E1phqmUG9Yz7jOuap4AxkTjsj1VH2Y7u9T5jhVCqrumKxLy9QwYbZQ==

-----END CERTIFICATE-----

l  导出公钥

使用openssl的x509命令从X.509证书文件导出公钥,命令如下。

使用-noout选项可以不显示证书文件内容,使用-pubkey选项可以以PEM格式输出证书的公钥信息。

exportcert=[前一步导出的证书文件名/完整路径]

publickey=[需要导出的公钥文件名/完整路径]

 

openssl x509 -in $exportcert -noout -pubkey > $publickey

示例:

exportcert=test.cer

publickey=public.pem

openssl x509 -in $exportcert -noout -pubkey > $publickey

导出后的公钥文件内容如下所示:

-----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwS18K4RE0BHIZ8a1h08w

...

zwIDAQAB

-----END PUBLIC KEY-----

2.2  验证签名

使用openssl的dgst命令的-verify选项对Java生成的签名结果进行验证,命令如下。

签名结果的生成算法:需要指定生成签名结果时使用的算法,dgst命令支持的算法包括“-md5|-md4|-md2|-sha1|-sha|-mdc2|-ripemd160|-dss1”等。当Java程序生成签名结果使用“MD5withRSA/Sha1withRSA/Sha256withRSA”算法时,该参数应指定为“md5/sha1/sha256”。

二进制的签名结果文件:需要指定Java生成的签名结果文件,该文件内容应为二进制,不能经过其他编码处理。

生成签名的原始数据文件:需要指定为Java生成签名结果时使用的原始数据保存的文件,文件内容应仅包含签名原始数据,不能包含多余的内容,如回车换行等。

algorithm=[签名结果的生成算法]

publickey=[前一步导出的公钥文件名/完整路径]

sign_data=[二进制的签名结果文件名/完整路径]

src_file=[生成签名的原始数据文件名/完整路径]

openssl dgst -$algorithm -verify $publickey -signature $sign_data < $src_file

示例:

algorithm=sha256

publickey=public.pem

sign_data=Sha256withRSA.sign

src_file=src.txt

openssl dgst -$algorithm -verify $publickey -signature $sign_data < $src_file

当验证签名通过时,提示“Verified OK”;当验证签名不通过时,提示“Verification Failure”;当指定的参数错误时,提示为其他。

3.  使用openssl生成签名

使用openssl命令可以生成签名。

首先需要将keystore文件中的私钥导出,再使用openssl命令根据私钥对数据进行签名。

3.1  导出私钥

在将keystore文件中的私钥导出时,需要先从keystore文件导出P12文件(包含私钥与证书),再从P12文件导出私钥。

l  导出P12文件

使用Java的keytool命令从keystore文件导出P12文件,命令如下。

keystore=[需要导出的keystore文件名/完整路径]

alias=[keystore文件需要导出证书文件的别名]

storepass=[keystore文件的密码]

keypass=[keystore文件中alias对应的密钥的密码]

p12_file=[生成的p12文件名/完整路径]

p12_pwd=[生成的p12文件的密码]

 

keytool -importkeystore -srckeystore $keystore -destkeystore $p12_file -srcstoretype JKS -deststoretype PKCS12 -srcstorepass $storepass -deststorepass $p12_pwd -srcalias $alias -destalias $alias -srckeypass $keypass -destkeypass $p12_pwd

示例:

 

keystore=test.jks

alias=test_alias

storepass=test_pwd

keypass=test_pwd

p12_file=tmp.p12

p12_pwd=test_p12_pwd

 

keytool -importkeystore -srckeystore $keystore -destkeystore $p12_file -srcstoretype JKS -deststoretype PKCS12 -srcstorepass $storepass -deststorepass $p12_pwd -srcalias $alias -destalias $alias -srckeypass $keypass -destkeypass $p12_pwd

导出成功时,无提示信息。

导出的P12文件为二进制形式。

l  导出私钥

使用openssl的pkcs12与rsa命令从P12文件导出私钥(生成keystore时选择的密钥算法为RSA,此时需要使用openssl的rsa命令),命令如下。

 

p12_file=[生成的p12文件名/完整路径]

p12_pwd=[生成的p12文件的密码]

tmp_pem=[导出的包含私钥与证书的临时文件名/完整路径]

final_pem=[导出的最终私钥文件名/完整路径]

 

openssl pkcs12 -in $p12_file -nodes -out $tmp_pem -passin pass:$p12_pwd

openssl rsa -in $tmp_pem > $final_pem

示例:

p12_file=tmp.p12

p12_pwd=test_p12_pwd

tmp_pem=tmp.pem

final_pem=final.pem

 

openssl pkcs12 -in $p12_file -nodes -out $tmp_pem -passin pass:$p12_pwd

openssl rsa -in $tmp_pem > $final_pem

首先使用openssl的pkcs12命令,将二进制形式的P12文件中的证书与私钥信息输出,指定-nodes选项,使输出私钥时不加密。pkcs12命令执行成功时,提示“MAC verified OK”。生成的包含证书与私钥信息的文件内容如下所示:

Bag Attributes

    friendlyName: test_alias

    localKeyID: 54 69 6D 65 20 31 35 35 33 36 39 30 32 30 37 33 39 34

Key Attributes: <No Attributes>

-----BEGIN PRIVATE KEY-----

MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCSXEfNNyk+5FtY

...

bqXK9vOoY4w+pCshUrDD7SFk

-----END PRIVATE KEY-----

Bag Attributes

    friendlyName: test_alias

    localKeyID: 54 69 6D 65 20 31 35 35 33 36 39 30 32 30 37 33 39 34

subject=/O=o_test/OU=ou_test/CN=cn.test

issuer=/O=o_test/OU=ou_test/CN=cn.test

-----BEGIN CERTIFICATE-----

MIIDCTCCAfGgAwIBAgIER2IkjTANBgkqhkiG9w0BAQsFADA1MQ8wDQYDVQQKDAZv

...

Sso9pt+lcXEhKw3zWw==

-----END CERTIFICATE-----

再使用openssl的rsa命令,将包含证书与私钥信息的文件内容导出为RSA私钥。rsa命令执行成功时,提示“writing RSA key”。

导出后的私钥文件内容如下所示:

-----BEGIN RSA PRIVATE KEY-----

MIIEpAIBAAKCAQEAwS18K4RE0BHIZ8a1h08wu8rypMbYirOQxcsdWEIl6eRz4m6o

...

t0p8ij9YNGzM5a8uDRZpIdOm3Z6X20d+PKj5Ucl+ns2SObSuSatSfw==

-----END RSA PRIVATE KEY-----

3.2  生成签名

使用openssl的dgst命令的-sign选项生成签名内容,命令如下。

签名结果的生成算法:略,见前文。

生成的签名内容的文件:指定签名内容需要保存到哪个文件。

生成签名的原始数据文件:指定生成签名内容时使用的原始数据的文件,文件内容应仅包含签名原始数据,不能包含多余的内容,如回车换行等。

algorithm=[签名结果的生成算法]

privatekey=[前一步导出的私钥文件名/完整路径]

sign_file=[生成的签名内容的文件名/完整路径]

src_file=[生成签名的原始数据文件名/完整路径]

 

openssl dgst -$algorithm -sign $privatekey <$src_file > $sign_file

示例:

algorithm=sha256

privatekey=final.pem

sign_file=sha256.sign

src_file=src.txt

 

openssl dgst -$algorithm -sign $privatekey <$src_file > $sign_file

生成的签名内容为二进制形式,可以与Java生成的签名内容比较是否一致。

发布了37 篇原创文章 · 获赞 0 · 访问量 2336

猜你喜欢

转载自blog.csdn.net/a82514921/article/details/104577856