-1

更新

我没有意识到这个问题仍然引起了人们的注意。

我前段时间发现并解决了这个问题,基本上是我不明白如何正确地做事。我没有为证书使用正确的文件类型,我没有正确读取私钥(应该使用PEM_read_RSAPrivateKey,不是PEM_read_PrivateKey),我没有正确计算签名等等。

我花了比它应该弄清楚的时间更多的时间。

感谢所有花时间回答的人。

原来的

我正在编写一些代码来对一些请求进行数字签名,但是我从代码生成的签名不正确,并且它与从命令行生成的签名不符。我怀疑问题是我没有正确读取私钥,但如果是这种情况,我希望得到一些错误,但我不是。

这是我第一次使用 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)。

我添加了代码来转储私钥以与密钥文件中的内容进行比较,并且它匹配,所以我正在正确读取密钥。

似乎有不少于 3 种不同的方式来使用 libcrypto 例程签署消息 - 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
4

2 回答 2

0

代码很好,生成的签名很好。您缺少的是每次生成签名时都会有所不同。如果您通过验证运行签名,它们都将起作用。我不知道原因,但我认为这样做是出于安全原因。

于 2019-01-27T17:11:30.003 回答
0

从这里的细节中不清楚如何处理签名数据(输入)。

openssll 命令会将这些读取为二进制(而不是文本),如果您在签署输入之前使用文本读取(即在 Windows 2 字符 CR/LF -> 转换为 1 CR),则可能会更改换行数据。

不签署相同的输入将创建一个差异摘要(显然)。

于 2020-04-01T17:06:40.147 回答