【问题标题】:How to calculate RSA public key fingerprint using OpenSSL?如何使用 OpenSSL 计算 RSA 公钥指纹?
【发布时间】:2020-01-18 07:30:39
【问题描述】:

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

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

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

【问题讨论】:

    标签: openssl rsa rsa-key-fingerprint


    【解决方案1】:

    在我自己的代码 (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");
    

    【讨论】:

    • 感谢您的回答。这给了我 openSSL 内部 RSA 结构,但没有指纹。我知道指纹基本上是密钥的哈希值,但我不知道必须为 EVP_Digest 函数提供什么确切格式的密钥。
    • 提示:EVP_PKEY_get1_RSA返回的rsa结构之后必须释放(openssl.org/docs/man1.0.2/man3/EVP_PKEY_get1_RSA.html
    【解决方案2】:

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

    可以使用BN_bn2hex(rsa.n) 从 PRSA 获取十六进制表示的模数,并且必须以“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;
    

    【讨论】:

      猜你喜欢
      • 2021-02-20
      • 2018-12-06
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      • 2021-10-08
      • 2018-10-07
      • 2014-10-26
      相关资源
      最近更新 更多