0

我对openssl接口和base64解码规则很陌生,并尝试使用解决方案验证用私钥(RS256 alg)签名的JWT:Segmentation Fault while verifying JWT token using a public key through openSSL in C++但是我的示例JWT失败了验证。

我只是没有base64 url​​解码功能,所以我按照我的理解手动执行了这些步骤-在调用我的base64解码功能之前,我替换了'-'->'+'和'_'->'/'。为了能够获得正确的签名长度,我还必须手动添加填充('=')。是否有一些我遗漏的base64 url​​解码规则,或者首先替换符号的方法是否有任何错误?

我首先尝试使用 RSA_verify,但错误是一样的。

使用 python 和在线 JWT 验证来验证相同的令牌和密钥,所以它们应该没问题。我在gdb上的十六进制b64解码和python上的urlsafe_b64decode之后打印了签名,并且值是相同的(除了python版本末尾的一些符号),这更让人相信替换策略应该是在职的。

   // This is the original token
   static constexpr const char* buffer_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXBpLXJlc291cmNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNTYzNDUwODkzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMzVhYmVjZDEtNjcxMi00M2M3LWE4MDItZjg3MGYzMTY4MmI0IiwiY2xpZW50X2lkIjoidGVzdCJ9.ZCXtI2nN-d0Cn5dgb3K9JMI41nrEaK_AVSMRG9c5cyZqXpnMQETfGcDEs0jPzmRh-jDc-Kuq53naOtjkItMcR_vYPn72dKZ4Fpp8mvOAZXypkVCLzof3Lsxrtqq9G3V4LNTuOHiXW_q-9mEu51zWg1HDr1-rSt3YXkFFSWp5e4MWS2TNP1MB7lBbZC-kdMZ_GqZ9lrfNo2YqJR7tqcHOrfOmFTzqxVivEB8s-A0iEv_MwdlS6LpJBKU9-d94i1P9Lsqzlg7b_0ekRoYJEG4DXeNp2zxxBxZ1u3FBlIbyJoOGDmX-EU4A5eh2RlDdEvG1YF_zcMARpP1bFV86WTSOuQ";

    // This is token with replaced symbols that I am testing before writing the method for url decode
    static constexpr const char* buffer_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXBpLXJlc291cmNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNTYzNDUwODkzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMzVhYmVjZDEtNjcxMi00M2M3LWE4MDItZjg3MGYzMTY4MmI0IiwiY2xpZW50X2lkIjoidGVzdCJ9.ZCXtI2nN+d0Cn5dgb3K9JMI41nrEaK/AVSMRG9c5cyZqXpnMQETfGcDEs0jPzmRh+jDc+Kuq53naOtjkItMcR/vYPn72dKZ4Fpp8mvOAZXypkVCLzof3Lsxrtqq9G3V4LNTuOHiXW/q+9mEu51zWg1HDr1+rSt3YXkFFSWp5e4MWS2TNP1MB7lBbZC+kdMZ/GqZ9lrfNo2YqJR7tqcHOrfOmFTzqxVivEB8s+A0iEv/MwdlS6LpJBKU9+d94i1P9Lsqzlg7b/0ekRoYJEG4DXeNp2zxxBxZ1u3FBlIbyJoOGDmX+EU4A5eh2RlDdEvG1YF/zcMARpP1bFV86WTSOuQ==";

    // this is how I create the RSA from a key, hopefully successfully because a key is returned with no error
    RSA* create_public_rsa(const unsigned char* p_key)
    {
       BIO *keybio = BIO_new_mem_buf(p_key, -1); // -1: assume string is null terminated
       if (!keybio)
       {
            return nullptr;
       }

       RSA* l_res = nullptr;

       l_res = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);

       BIO_free(keybio);

       return l_res;
    }

    bool RSAVerifySignature(RSA* rsa, std::string const& token)
    {
       auto pub_key_handle = std::shared_ptr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
       if (!pub_key_handle)
       {
           return false;
       }

       RSA_up_ref(rsa);
       EVP_PKEY_assign_RSA(pub_key_handle.get(), rsa);


       std::string decoded_header(token, 0, token.find('.'));
       std::string decoded_body;
       decoded_body.append(token.begin()+ token.find('.')+1, token.begin() + token.rfind('.')-1);
       std::string sig;
       sig.append(token.begin() + token.rfind('.') + 1, token.end());
       std::string sig_decoded;
       base64_decode(sig.c_str(), sig.size(), sig_decoded);

       EVP_MD_CTX* l_ctx = EVP_MD_CTX_create();
       EVP_MD_CTX_init(l_ctx);

       EVP_PKEY_CTX *pctx;
       if (1 != EVP_DigestVerifyInit(l_ctx, /*&pctx*/nullptr, 
 EVP_sha256(), nullptr, pub_key_handle.get())) return false;
       //pub_key_handle.reset();
       if (1 != EVP_DigestVerifyUpdate(l_ctx, reinterpret_cast<const unsigned char*>(decoded_header.data()), decoded_header.length())) return false;
       if (1 != EVP_DigestVerifyUpdate(l_ctx, ".", 1)) return false;
       if (1 != EVP_DigestVerifyUpdate(l_ctx, reinterpret_cast<const unsigned char*>(decoded_body.data()), decoded_body.length())) return false;
       if(1 == EVP_DigestVerifyFinal(l_ctx, reinterpret_cast<const unsigned char*>(sig_decoded.data()), sig_decoded.length())) return true;

       // ERR_print_errors_fp(stdout);

       ERR_load_crypto_strings();
       char err[130];
       while(auto e = ERR_get_error())
       {
          ERR_error_string(e, err);
          fprintf(stderr, "Error verifying message: %s\n", err);
       }

       return false;
    }  

错误是:

header: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
body: eyJhdWQiOlsiYXBpLXJlc291cmNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNTYzNDUwODkzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMzVhYmVjZDEtNjcxMi00M2M3LWE4MDItZjg3MGYzMTY4MmI0IiwiY2xpZW50X2lkIjoidGVzdCJ
sig: ZCXtI2nN+d0Cn5dgb3K9JMI41nrEaK/AVSMRG9c5cyZqXpnMQETfGcDEs0jPzmRh+jDc+Kuq53naOtjkItMcR/vYPn72dKZ4Fpp8mvOAZXypkVCLzof3Lsxrtqq9G3V4LNTuOHiXW/q+9mEu51zWg1HDr1+rSt3YXkFFSWp5e4MWS2TNP1MB7lBbZC+kdMZ/GqZ9lrfNo2YqJR7tqcHOrfOmFTzqxVivEB8s+A0iEv/MwdlS6LpJBKU9+d94i1P9Lsqzlg7b/0ekRoYJEG4DXeNp2zxxBxZ1u3FBlIbyJoOGDmX+EU4A5eh2RlDdEvG1YF/zcMARpP1bFV86WTSOuQ==
Error verifying message: error:04091068:rsa routines:INT_RSA_VERIFY:bad signature
error:04091068:rsa routines:INT_RSA_VERIFY:bad signature
4

1 回答 1

1

这是我使用的 base64_url_decode。我没有写它,但我找不到我从哪里得到它。

使用下面的代码,你还有问题吗?

/*
Base64 translates 24 bits into 4 ASCII characters at a time. First,
3 8-bit bytes are treated as 4 6-bit groups. Those 4 groups are
translated into ASCII characters. That is, each 6-bit number is treated
as an index into the ASCII character array.

If the final set of bits is less 8 or 16 instead of 24, traditional base64
would add a padding character. However, if the length of the data is
known, then padding can be eliminated.

One difference between the "standard" Base64 is two characters are different.
See RFC 4648 for details.
This is how we end up with the Base64 URL encoding.
*/

const char base64_url_alphabet[] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};

std::string base64_url_encode(const std::string & in) {
  std::string out;
  int val =0, valb=-6;
  size_t len = in.length();
  unsigned int i = 0;
  for (i = 0; i < len; i++) {
    unsigned char c = in[i];
    val = (val<<8) + c;
    valb += 8;
    while (valb >= 0) {
      out.push_back(base64_url_alphabet[(val>>valb)&0x3F]);
      valb -= 6;
    }
  }
  if (valb > -6) {
    out.push_back(base64_url_alphabet[((val<<8)>>(valb+8))&0x3F]);
  }
  return out;
}

std::string base64_url_decode(const std::string & in) {
  std::string out;
  std::vector<int> T(256, -1);
  unsigned int i;
  for (i =0; i < 64; i++) T[base64_url_alphabet[i]] = i;

  int val = 0, valb = -8;
  for (i = 0; i < in.length(); i++) {
    unsigned char c = in[i];
    if (T[c] == -1) break;
    val = (val<<6) + T[c];
    valb += 6;
    if (valb >= 0) {
      out.push_back(char((val>>valb)&0xFF));
      valb -= 8;
    }
  }
  return out;
}

更新:

您的问题不是 base64 解码,而是这一行:

decoded_body.append(token.begin()+ token.find('.')+1, token.begin() + token.rfind('.')-1);

你出局了。decoded_body 缺少最后一个字符。如果您将其更改为以下内容,它将正常工作:

decoded_body.append(token.begin()+ token.find('.')+1, token.begin() + token.rfind('.'));
于 2019-08-01T17:49:43.093 回答