我正在处理一些代码以对一些请求进行数字签名,但是我从代码生成的签名不正确,并且它与从命令行生成的签名不匹配。我怀疑的问题是我没有正确读取私钥,但是如果是这种情况,我会期望找回一些错误,但我不是。
这是我第一次使用openssl,并且我确定自己做的事情不正确,但是我还不了解提出聪明的问题。
我创建了一个MessageSigner
类来处理libcrypto杂务。私钥和证书是该类的属性:
class MessageSigner
{
...
private:
EVP_PKEY *private_key;
X509 *certificate;
};
创建实例后,我将它们初始化为NULL
:
MessageSigner::MessageSigner() :
private_key( NULL ),
certificate( NULL )
{
}
我按如下方式加载私钥:
void MessageSigner::addKeyFile( const std::string& filename, const std::string& passphrase )
{
FILE *fp = ::fopen( keyfile.c_str(), "r" );
if ( !fp )
// throw exception
private_key = PEM_read_PrivateKey( fp, NULL, NULL, passphrase.c_str() );
::fclose( fp );
if ( !private_key )
// throw exception
}
我将签名生成为
void MessageSigner::signMessage( const std::vector< unsigned char >& msg, std::vector< unsigned char >& signature )
{
unsigned char *msgbuf = new unsigned char [msg.size()];
std::copy( msg.begin(), msg.end(), msgbuf );
unsigned char *sigbuf;
size_t sigbuf_length;
EVP_MD_CTX *ctx = EVP_MT_CTX_create();
if ( !ctx )
// throw exception
if ( EVP_DigestSignInit( ctx, NULL, EVP_sha256(), NULL, private_key ) != 1)
// throw exception
if ( EVP_DigestSignUpdate( ctx, msgbuf, msg.size() ) != 1 )
// throw exception
if ( EVP_DigestSignFinal( ctx, NULL, &sigbuf_length ) != 1 )
// throw exception
sigbuf = (unsigned char *)OPENSSL_malloc( sizeof *sigbuf * sigbuf_length );
if ( !sigbuf )
// throw exception
if ( EVP_DigestSignFinal( ctx, sigbuf, &sigbuf_length ) != 1 )
// throw exception
std::copy( sigbuf, sigbuf + sigbbuf_length, std::back_inserter( signature ) );
EVP_MD_CTX_destroy( ctx );
OPENSSL_free( sigbuf ); // yes, there's potential for a memory leak, but I'm just trying to get this bastard to work.
}
同样,此代码生成的签名与
的结果不吻合openssl rsautl -sign -inkey keyfile.pem -keyform PEM -in msg.txt -out signature
我确信问题出在我如何加载私钥上,但是到目前为止,我所看到的所有示例都表明这应该可行。
我一直盯着这个,把头发拉了两天。 任何建议,提示,指导,粗鲁的评论等,将不胜感激。
谢谢。
编辑
一些例子应该说明我要面对的问题。
以下是为给定消息生成的SignedInfo
:
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="alws soapenv"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#TS-5b171864-232b-11e9-846f-00505695541c">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2006/12/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsse alws soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestValue>eeLn6ak1glbbbWE48q7olsxO0CO/fL85bZ+8hzcjrvE=
</ds:DigestValue>
<ds:DigestMethod Algorithm="https//www.w3.org/2001/04/xmlenc#sha256"/>
</ds:Reference>
</ds:SignedInfo>
摘要值是根据时间戳计算的:
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="TS-5b171864-232b-11e9-846f-00505695541c">
<wsu:Created>2019-01-28T18:34:33Z</wsu:Created>
<wsu:Expires>2019-01-28T18:34:38Z</wsu:Expires>
</wsu:Timestamp>
当我这样从命令行计算该时间戳的摘要时:
$ openssl dgst -sha256 -binary timestamp > timestamp.dgst
我得到相同的摘要值:
$ openssl base64 -in timestamp.dgst
eeLn6ak1glbbbWE48q7olsxO0CO/fL85bZ+8hzcjrvE=
到目前为止,一切都很好。该哈希值将添加到SignedInfo
,然后我将SignedInfo
的哈希值:
cQaWLGHi8D/c1kXPG9i49xzAupeBuypvMvuMQlzA/wo=
并使用它来生成签名。现在,这里的事情完全脱离了轨道。当使用上述代码生成签名时,我得到:
HtQ4LkYq4Eao4bMOpV4SBpMxHi2a+0ilxDXS9jIQZWdCC8HCNlpvVU4rWMZG2Zd/
LplCWmUHIaB35FKv6uKjCjJPVDAJT2agyp7FnSKxaBI44Y/YsdvKyxJTAMiAlF8i
dd1MB8ljYsfayrzq5e76kt2cbHlYkT/RM3SvwJtjZiYsNpfcXD0Bi6JhRshHxQ8s
6/errruOe7jUqbKh7UOPJokadCX0OTSSwRgcs+sm7VjnS9MYILaGzFFT3Js9xI6d
TL4B6A/JGIkEqLO+GA1lrokAeIBr9OVUu7OEzaBb7DaiP9Gv1diu0j1sbZ4uT5Cf
CjYJPYU72Xx8F+MKdSJteg==
使用命令行工具时,我得到:
PvfCDqPl86/8USbFU0XR5r1Dhl5JbWd2va3L4W1IW1zw6xdes04F4lYjol6gMKio
jyr8DdmWBquroVlo4vW8kmhr6760qMcpK6mfsZ26ftu7XRC+Z4b9ge6ICOemsGlE
04Yoh9EpECP+ei5yS4E1sbntteiSoQcjotmVcIbPaEG5DIDcd4JKfoCWmsnuZESs
qctIJAQy4YY9HJsVGJ2JG7QashFcEQJabtInFgYeKuxla0ZSXBfOBkwHZT/cSv+k
n/NqPMCyEl4B2LiPBVa36GaTUd6fx0SXnIh0Fm+jw6b6j3EjU0QfMJ/JBAlL+oWZ
fXO/pS5L7W+OWk8Fh//iKA==
当我验证命令行上生成的签名时,我会得到原始的哈希值:
$ openssl base64 -d -in signature.b64 > signature.reversed
$ openssl rsautl -verify -inkey cert.pem -certin -in signature.reversed > signature.reversed.dgst
$ openssl base64 -in signature.reversed.dgst
cQaWLGHi8D/c1kXPG9i49xzAupeBuypvMvuMQlzA/wo=
当我尝试验证由我的代码生成的签名时,我不获取原始哈希值:
$ openssl base64 -d -in badsig.b64 > badsig
$ openssl rsautl -verify -inkey cert.pem -certin -in badsig > badsig.dgst
$ openssl base64 -in badsig.dgst
MDEwDQYJYIZIAWUDBAIBBQAEIHEGlixh4vA/3NZFzxvYuPccwLqXgbsqbzL7jEJc
wP8K
尤其令人沮丧的是,自从我在创建签名后添加了代码以验证签名,并且该代码通过了。但这显然是错误的-远程服务拒绝我们的请求,因为哈希值不匹配,并且与openssl命令行工具生成的内容也不匹配。我已验证命令行工具和我正在使用的库是同一版本(1.0.1)。
我添加了代码以转储私钥以与密钥文件中的内容进行比较,并且该私钥与之匹配,因此我正在正确读取密钥。
使用libcrypto例程对消息进行签名的方法似乎不少于3种-EVP_Sign(),EVP_DigestSign(),EVP_PKEY_sign()-我不确定哪种方法适用于我的情况。这三个都给我不好的签名(也就是说,当使用公钥解密时,不会导致原始哈希值)。
因此,如果有人可以指出我正在做的任何事情,那显然是错误的,我将不胜感激。
更多编辑
还尝试使用dgst
进行验证,如下所示,再次使用上面的SignedInfo
块:
$ openssl dgst -sha256 -binary -sign fx-realtime.fundsxpress.com.pem -out signature signedinfo
$ openssl dgst -sha256 -binary -verify publickey.pem -signature signature signedinfo
Verified OK
反转我的代码生成的签名:
$ echo -n 'XfgP1A08UTwz3sUHIVvvV+fq1n3act6+lVBZ8ieDtgh28k1r1/M0tm9MntvK+Hm4
> Be+LjguX2gxhZ4PvVcoCBCugDIsrhxplDeB4bYeY2PEedQL6+IZFX+kFrz6o3RQa
> W7sXK7czogxWpdLAmKnhDJOk2BmKFihkRMTjo9D4z/qylZI9nnX29HNdg3uV2BYw
> zHh8GvYO8fy1ugqfFW80na+hLBAtBP6fwTTv10DS2L8n+ixQcnxlKW5pyBOXlR/r
> mZEqwU+A996G0573HkGFeFvXzArlRFg/7mkKoyUHyqyDzkf5eC+vTnpEy1CP75Yc
> lvd7ldSrwREisPnyxu47sg=='> computed_signature.b64
$ openssl base64 -e -in computed_signature.b64 > computed_signature
$ openssl dgst -sha256 -binary -verify publickey.pem -signature computed_signature signedinfo
Verification Failure
答案 0 :(得分:0)
该代码很好,并且生成的签名也很好。您缺少的是,每次生成签名时,签名都会不同。如果您通过验证运行签名,那么它们将全部起作用。我不知道为什么这样做,但我认为这样做有一个安全原因。
答案 1 :(得分:0)
从此处的详细信息尚不清楚,如何处理已签名的数据(输入)。
openssll命令会将其读取为二进制(不是文本),并且如果在签署输入之前使用文本读取(例如,在Windows 2字符CR / LF->转换为1 CR)上使用文本读取,则可能会有换行数据被更改。 。
不对同一输入进行签名将创建差异摘要(显然)。