在 OpenSSL 中验证 RSA public 密钥?

Verify a RSA public key in OpenSSL?

我有一个 EVP_PKEY,只有 RSA 密钥的 public 部分。我从 DER 编码的 SubjectPublicKeyInfo 结构中提取了 public 部分。这就是我现在拥有的:

unsigned char publicKey[] = {0x30, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, ...}
size_t publicKeyLength = 92;
unsigned char* publicKeyCopy = new unsigned char[publicKeyLength];
memcpy(publicKeyCopy, publicKey, publicKeyLength);

RSA *rsa;
rsa = d2i_RSA_PUBKEY(NULL, (unsigned char const **) &pubKey, pubKeyLen);
EVP_PKEY *pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);

我知道你可以使用 RSA_check_key to verify a RSA private key 但文档说“它不适用于只有模数和 public 的 RSA public 密钥填充指数元素”。

那么,是否可以在没有私有部分的情况下验证密钥?因为如您所见,我只有 EVP_PKEY 的 public 部分。我想知道,这可能吗?您将在 EVP_PKEY 的 public 部分验证什么?

你可以看到这个问题的答案 但完整的密钥已经过验证(私有和 public 部分)。

小心 本题贴出的原代码有BUG。这是因为在内部 d2i_RSA_PUBKEY 使用 d2i_PUBKEYd2i_PUBKEY 使用 d2i_X509_PUBKEY (在 x_pubkey.c 中)。如果您阅读 documentation for d2i_X509,您将看到下一个 "WARNING: The use of temporary variable is mandatory. A common mistake is to attempt to use a buffer directly..."。 因此,更正后的代码必须使用 publicKeyCopy 的临时副本,使用后您可以安全地删除 publicKeyCopy:

在@jww 的帮助下回答。我想到了这个解决方案,希望没问题:

bool isValidPublicKeyOnly(EVP_PKEY *pkey) {
    //EVP_PKEY_get_type from 
    int type = EVP_PKEY_get_type(pkey); //checks nullptr
    if (type != EVP_PKEY_RSA && type != EVP_PKEY_RSA2) {
        //not RSA
        return false;
    }

    RSA *rsa = EVP_PKEY_get1_RSA(pkey);
    if (!rsa) {
        return false;
    }

    bool isValid = isValidRSAPublicKeyOnly(rsa);
    RSA_free(rsa);
    return isValid;
}

bool isValidRSAPublicKeyOnly(RSA *rsa) {
    //from rsa_ameth.c do_rsa_print : has a private key
    //from rsa_chk.c RSA_check_key : doesn't have n (modulus) and e (public exponent)
    if (!rsa || rsa->d || !rsa->n || !rsa->e) {
        return false;
    }
    //from http://rt.openssl.org/Ticket/Display.html?user=guest&pass=guest&id=1454
    //doesnt have a valid public exponent
    return BN_is_odd(rsa->e) && !BN_is_one(rsa->e);
}

Beware The original code posted in this question has a BUG...

我只是要对此发表评论,并向您展示如何处理它。

unsigned char publicKey[] = {0x30, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, ...}
size_t publicKeyLength = sizeof(publicKey);

unsigned char* t = publicKey;
rsa = d2i_RSA_PUBKEY(NULL, &t, pubKeyLen);

在内部,临时指针t被递增,所以它被浪费了。如果一切按预期进行,它将指向缓冲区之后的某个位置。你应该在函数执行后找到(size_t)t - (size_t)publicKey == publicKeyLength

因为你用的是临时指针,原来的指针publicKey还是不错的。如果内存中有连续的键,则可以使用t解析下一个键。

不需要复制数据。


我认为第二种选择是使用内存 BIOd2i_RSA_PUBKEY_bio。类似于:

BIO* bio = BIO_new_mem_buf(publicKey, (int)publicKeyLength);
ASSERT(bio != NULL);

RSA* rsa = d2i_RSA_PUBKEY_bio(bio, NULL);
ASSERT(rsa != NULL);

/* ... */

RSA_free(rsa);
BIO_free(bio);

get1 会增加引用计数,因此您需要在 EVP_PKEY*RSA* 上调用 free

我有一个类似的问题,我认为展示我对这个问题的解决方案可能是明智的。与 lmiguelmh 的解决方案不同,这个解决方案在 C 中有效。

int checkRsaPublic(RSA *rsa, int debug) {
    if (!rsa) {
        printf("ERROR: RSA key not defined!\n");
        return 0;
    }
    //key
    const BIGNUM *n;
    const BIGNUM *e;
    const BIGNUM *d;

    //factors
    const BIGNUM *p;
    const BIGNUM *q;

    //crt_params
    const BIGNUM *dmp1;
    const BIGNUM *dmq1;
    const BIGNUM *iqmp;

    RSA_get0_key(rsa, &n, &e, &d);
    RSA_get0_factors(rsa, &p, &q);
    RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);

    if (debug) {
        if (n) {
            printf("n is %s\n", BN_bn2hex(n));
        }
        if (e) {
            printf("e is %s\n", BN_bn2hex(e));
        }
        if (d) {
            printf("d is %s\n", BN_bn2hex(d));
        }
        if (p) {
            printf("p is %s\n", BN_bn2hex(p));
        }
        if (q) {
            printf("q is %s\n", BN_bn2hex(q));
        }
        if (dmp1) {
            printf("dmp1 is %s\n", BN_bn2hex(dmp1));
        }
        if (dmq1) {
            printf("dmq1 is %s\n", BN_bn2hex(dmq1));
        }
        if (iqmp) {
            printf("iqmp is %s\n", BN_bn2hex(iqmp));
        }
    }

    //RSA_check_key : doesn't have n (modulus) and e (public exponent)
    if (d || !n || !e) {
        printf("ERROR: RSA public key not well defined!\n");
        return 0;
    }

    if (BN_is_odd(e) && !BN_is_one(e)) {
        return 1;
    }

    printf("ERROR: Invalid public exponent.");
    return 0;
}