0

我在 EVP_PKEY 结构中有一个 RSA 公钥(从以 -----BEGIN PUBLIC KEY----- 开头的 PEM 文件加载)。现在我希望能够使用 OpenSSL API 从我的代码中显示该密钥的指纹。(目的是让运营商在信任之前使用 RS256 验证 JWT 的密钥)。

不幸的是,到目前为止,我发现的所有资源要么在命令行上使用 ssh-keygen,要么使用 X.509 证书的指纹而不是公钥。

那么如何使用 OpenSSL API 获取存储在 EVP_PKEY 结构中的公钥的 RSA 公钥指纹?

4

2 回答 2

0

虽然我仍然不确定这是否是“官方方式”/常用方式(适合写“这是钥匙的指纹”),但我在“Deutsches Forschungsnetz”找到了参考(https://www. dfn-cert.de/informationen/themen/verschluesselung_und_pki/openssl-kurzreferenz.html),他们使用密钥模数的哈希值。

十六进制表示中的模数可以从 PRSA 获取,BN_bn2hex(rsa.n)并且必须以“Modulus=”为前缀,并以 CR/LF 为后缀,以产生与命令行相同的输出。这是我生成的 Delphi 代码:

function GetRsaModulusFingerprint(theRsa: PRSA; md: PEVP_MD; useLowerCase:
    Boolean; const separator: string): string;
var
  len: integer;
  modulusStr: PAnsiChar;
  buff: AnsiString;
  mdValue: array [0..EVP_MAX_MD_SIZE-1] of Byte;
  ctx: EVP_MD_CTX;
  mdLen: Cardinal;
  I: Integer;
begin
  // This func should produce same result as commandline
  // for public key:
  //  openssl rsa -noout -modulus -pubin -in server.pubkey |openssl sha256 -c
  // for private key:
  //  openssl rsa -noout -modulus -in server.privkey |openssl sha256 -c

  Result := '';
  if not Assigned(theRsa) then
    Exit;

  // get hex representation of modulus
  modulusStr := BN_bn2hex(theRsa.n);
  // bring it in same format as commanline
  buff := 'Modulus='+ AnsiString(modulusStr) + #13#10;
  // and free the string returned by BN_bn2hex using OPENSSL_free
  // (in openSSL1.0.2r crypto.h OPENSSL_free is a macro pointing to CRYPTO_free)
  CRYPTO_free(modulusStr);

  // calculate hash
  len := length(buff);
  if len >= 0 then
  begin
    EVP_MD_CTX_init(@ctx);
    EVP_DigestInit_ex(@ctx, md, nil);
    EVP_DigestUpdate(@ctx, PAnsiChar(buff), len);
    EVP_DigestFinal_ex(@ctx, @mdValue[0], mdLen);
    EVP_MD_CTX_cleanup(@ctx);

    //output as hex with separator
    result := '';
    if mdLen > 0 then
      result := IntToHex(mdValue[0]);
    for I := 1 to mdLen-1 do
      result := result + separator + IntToHex(mdValue[I]);

    if useLowerCase then
      result := result.ToLower();
  end;
end;
于 2019-11-22T14:52:20.943 回答
0

在我自己的代码(C++)中,我使用了下面的代码,它接受一个 EVP_PKEY 对象并返回一个 RSA 对象:

using evp_pkey_ptr = std::unique_ptr<EVP_PKEY>;
using rsa_ptr = std::unique_ptr<RSA>;

rsa_ptr evp_get_rsa(const evp_pkey_ptr& evp) {
  RSA * rsa = EVP_PKEY_get1_RSA(evp.get());
  return rsa_ptr(rsa);
}

关于您验证 JWT 的目的。

我是这样做的:

// get jwt payload from jwt data
std::string payload_b64 = jwt[0] + "." + jwt[1];

// get jwt signature from jwt data
std::string signature_b64 = from_base64uri(jwt[2].c_str(), jwt[2].size());

// obtain RSA object from EVP
RSA * rsa = EVP_PKEY_get1_RSA(evp)

// create a digest of the jwt payload, using SHA256
digest = SHA256_Init, SHA256_Update, SHA256_Final, etc

// get byte representation of the jwt signature
sig_bytes = from_base64(payload_b64);

// call OpenSSL API to verify digest matches received signature
ERR_clear_error();
int rc = RSA_verify(NID_sha256,
                    (const unsigned char*) digest.data(), digest.size(),
                    (const unsigned char*) sig_bytes.data(), sig_bytes.size(), 
                    rsa);
if (1 != rc)
  printf("RSA_verify");
于 2019-09-19T09:10:41.377 回答