You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CryptDecodeObjectEx解析RSA公钥时的长度与内容异常问题

RSA公钥解码问题解决

问题根源

你调用CryptDecodeObjectEx时使用了RSA_CSP_PUBLICKEYBLOB作为解码类型,这会将公钥数据解码为Windows CSP专用的PUBLICKEYBLOB结构,而非证书管理器显示的原始RSA公钥(仅包含模数n和指数e)。该结构包含6字节额外头部元数据,这就是解码结果比证书管理器公钥长6字节的原因,同时结构内的额外信息也导致内容不匹配。

修正方案

1. 解码为原始RSA公钥结构

将解码类型替换为szOID_RSA_RSA,直接解码出包含模数和指数的RSA公钥结构,再提取对应数据转换成十六进制字符串。

2. 修复ByteToStr函数的长度检查错误

原函数中用strlen(sz) < cb检查缓冲区长度是错误的——malloc分配的内存未初始化,strlen会读取到随机位置的终止符,导致判断失效。改为传入缓冲区的实际大小进行检查。

修正后代码示例

// 修复后的ByteToStr函数
BOOL ByteToStr(DWORD cb, void* pv, LPSTR sz, DWORD szBufferSize, int* size_sz, BOOL addSpaces, BOOL upperCaseHexDigits)
{
    BYTE* pb = (BYTE*)pv;
    DWORD i;
    int b;
    int outCharCount = 0;

    // 检查缓冲区是否足够:每个字节占2个十六进制字符,若加空格则多1个,最后加终止符
    DWORD requiredSize = cb * (addSpaces ? 3 : 2) + 1;
    if (szBufferSize < requiredSize) {
        MyHandleError(L"缓冲区长度不足,无法容纳转换后的字符串");
        return FALSE;
    }

    for (i = 0; i < cb; i++) {
        // 处理高4位
        b = (*pb & 0xF0) >> 4;
        *sz++ = (b <= 9) ? (b + '0') : ((upperCaseHexDigits) ? (b - 10 + 'A') : (b - 10 + 'a'));
        outCharCount++;

        // 处理低4位
        b = *pb & 0x0F;
        *sz++ = (b <= 9) ? (b + '0') : ((upperCaseHexDigits) ? (b - 10 + 'A') : (b - 10 + 'a'));
        outCharCount++;

        pb++;
        if (addSpaces && i != cb - 1) { // 最后一个字节后不加空格
            *sz++ = ' ';
            outCharCount++;
        }
    }
    *sz++ = 0;
    outCharCount++;
    *size_sz = outCharCount;

    return TRUE;
}

// 解码RSA公钥的核心代码
publicKeyInfo = pCertContext->pCertInfo->SubjectPublicKeyInfo;

RSAPUBKEY* pRsaPubKey = NULL;
DWORD cbRsaPubKey = 0;

// 解码为RSA公钥结构
if (!CryptDecodeObjectEx(
    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
    szOID_RSA_RSA,
    publicKeyInfo.PublicKey.pbData,
    publicKeyInfo.PublicKey.cbData,
    CRYPT_ENCODE_ALLOC_FLAG,
    NULL,
    (void**)&pRsaPubKey,
    &cbRsaPubKey))
{
    MyHandleError(L"解码RSA公钥失败");
    return;
}

// 提取模数:紧跟在RSAPUBKEY结构之后,长度为密钥位数/8
DWORD cbModulus = pRsaPubKey->bitlen / 8;
BYTE* pModulus = (BYTE*)pRsaPubKey + sizeof(RSAPUBKEY);

// 提取指数:转换为大端字节序(去除前导0)
BYTE expBytes[4];
expBytes[3] = (pRsaPubKey->pubexp >> 0) & 0xFF;
expBytes[2] = (pRsaPubKey->pubexp >> 8) & 0xFF;
expBytes[1] = (pRsaPubKey->pubexp >> 16) & 0xFF;
expBytes[0] = (pRsaPubKey->pubexp >> 24) & 0xFF;
DWORD cbExp = 4;
// 跳过前导0
while (cbExp > 0 && expBytes[4 - cbExp] == 0) {
    cbExp--;
}
BYTE* pExp = expBytes + (4 - cbExp);

// 合并模数和指数,准备转换为十六进制字符串
DWORD totalLen = cbModulus + cbExp;
BYTE* combinedKey = (BYTE*)malloc(totalLen);
memcpy(combinedKey, pModulus, cbModulus);
memcpy(combinedKey + cbModulus, pExp, cbExp);

// 转换为空格分隔的十六进制字符串
DWORD strBufferSize = totalLen * 3 + 1;
LPSTR sz = (char*)malloc(strBufferSize);
int size_sz = 0;
BOOL success = ByteToStr(totalLen, combinedKey, sz, strBufferSize, &size_sz, TRUE, FALSE);

// 此时sz就是与证书管理器一致的公钥十六进制字符串
// 这里可以添加输出或使用sz的逻辑

// 释放内存
free(sz);
free(combinedKey);
LocalFree((HANDLE)pRsaPubKey);

说明

  • 解码后的RSAPUBKEY结构包含公钥的位数和指数,模数数据紧跟在结构之后。
  • 指数存储为DWORD类型,需要转换为大端字节序并去除前导0,才能匹配证书管理器显示的短格式指数。
  • 修复后的ByteToStr函数会正确检查缓冲区长度,避免内存越界,同时最后一个字节后不再添加多余空格。

内容的提问来源于stack exchange,提问作者John Rainey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 10:36:03