队列的结构体封装实现(啊哈算法版,以解密QQ号为例)

本文介绍了一个简单的队列操作实现,包括队列的初始化、元素的入队和出队过程,并通过一个具体的C语言示例展示了如何使用循环队列进行数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <stdio.h>
#include <stdlib.h>
struct queue{
    int data[100];
    int head;
    int tail;
};
int main()
{
    struct queue q;
    //队列初始化,head=tail=0
    //tail总指向队列的下一个位置
    q.head=0;
    q.tail=0;
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&q.data[q.tail++]);
    while(q.head<q.tail){
        printf("%d",q.data[q.head++]);
        q.data[q.tail++]=q.data[q.head++];
    }
    return 0;
}

<think>我们正在使用OpenSSL的EVP API,但有时需要从EVP_PKEY结构中提取底层的RSA结构(如,使用一些传统的RSA函数)。 注意:在OpenSSL 1.1.0及以上本中,RSA结构已经变得不透明,不能直接访问其内部成员。因此,我们使用一组函数来获取RSA结构。 另外,从EVP_PKEY中提取RSA密钥有两种情况:提取公钥或私钥。 步骤: 1. 首先检查EVP_PKEY中的密钥类型是否为RSA。 2. 使用EVP_PKEY_get1_RSA()函数来获取RSA结构,并增加其引用计数(使用完毕后需要释放)。 函数说明: - RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey); 该函数返回EVP_PKEY关联的RSA结构,并将引用计数加1。因此,使用完毕后必须调用RSA_free()来减少引用计数。 注意:在OpenSSL 3.0中,由于提供了Provider机制,传统算法(如RSA)被实现为提供者,因此EVP_PKEY可能并不直接包含RSA结构,但EVP_PKEY_get1_RSA()函数仍然可用,它会返回一个传统形式的RSA结构(属于遗留的RSA对象)。 示代码: 假设我们已经有一个EVP_PKEY *pkey(可能是公钥或私钥),我们从中提取RSA结构: ```c #include <openssl/evp.h> #include <openssl/rsa.h> // 从EVP_PKEY中提取RSA结构(可能是公钥或私钥,取决于pkey的内容) RSA *get_rsa_from_evp_pkey(EVP_PKEY *pkey) { if (!pkey) { return NULL; } // 检查密钥类型 if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) { fprintf(stderr, "Not an RSA key\n"); return NULL; } // 获取RSA结构并增加引用计数 RSA *rsa = EVP_PKEY_get1_RSA(pkey); if (!rsa) { fprintf(stderr, "Failed to get RSA key\n"); return NULL; } return rsa; // 注意:调用者必须使用RSA_free(rsa)释放 } ``` 使用示: ```c RSA *rsa_key = get_rsa_from_evp_pkey(pKey); if (rsa_key) { // 使用rsa_key进行一些操作,如使用RSA_public_encrypt或RSA_private_decrypt等 // ... // 最后释放RSA结构 RSA_free(rsa_key); } ``` 重要注意事项: - 在OpenSSL 1.1.0及以上本中,RSA结构是不透明的,因此不能直接访问其内部字段(如n, e, d等)。如果需要访问这些字段,应使用相应的获取函数,如: const BIGNUM *RSA_get0_n(const RSA *rsa); const BIGNUM *RSA_get0_e(const RSA *rsa); const BIGNUM *RSA_get0_d(const RSA *rsa); // 等等 - 如果你需要修改RSA密钥(如设置新的参数),则应该使用相应的设置函数(如RSA_set0_key等),并且注意这些函数会转移传入的BIGNUM的所有权。 - 在OpenSSL 3.0中,更推荐使用EVP API,而不是传统的RSA_*函数。但是,如果必须使用传统函数(比如兼容旧代码),则可以使用上述方法提取RSA结构。 - 引用计数:EVP_PKEY_get1_RSA()会增加返回的RSA结构的引用计数,因此每次调用都必须对应一次RSA_free()。注意不要多次释放。 如果使用提取出来的RSA结构进行加密/解密操作,可以使用传统的RSA函数,如: - RSA_public_encrypt() 和 RSA_private_decrypt() - 注意这些函数需要指定填充方式(如RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING等),并且有输入长度限制(引用[2]中提到)。 但请注意:EVP API是更推荐的方式,因为它能够支持更多的算法和更安全的默认配置。 引用说明: 引用[2]中提到了RSA_public_encrypt()和RSA_private_decrypt()的使用,以及填充方式和输入长度的限制。这些信息仍然适用于从EVP_PKEY中提取的RSA结构。 最后,请确保在程序结束时正确清理资源。 相关问题: 1. 如何从RSA结构中获取公钥的模数和指数? 2. 在OpenSSL 3.0中,使用传统RSA函数EVP API相比有什么缺点? 3. 如何将RSA结构转换回EVP_PKEY结构? 4. 在非对称加密中,RSA和ECC在性能和安全性上有何差异?</think>### 从 EVP_PKEY 中提取 RSA 结构的 OpenSSL 实现 在 OpenSSL 中,从 `EVP_PKEY` 结构中提取底层的 `RSA` 结构需要根据 OpenSSL 本选择不同的 API。以下是完整实现方案: #### 1. **OpenSSL 1.1.0+ 本(推荐)** ```c #include <openssl/evp.h> #include <openssl/rsa.h> RSA* get_rsa_from_evp(EVP_PKEY *pkey) { // 检查密钥类型 if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) { fprintf(stderr, "错误:非 RSA 密钥类型\n"); return NULL; } // 提取 RSA 结构(增加引用计数) RSA *rsa = EVP_PKEY_get1_RSA(pkey); if (!rsa) { fprintf(stderr, "错误:提取 RSA 结构失败\n"); ERR_print_errors_fp(stderr); } return rsa; // 调用者需使用 RSA_free(rsa) 释放 } ``` #### 2. **OpenSSL 1.0.x 及更早本** ```c RSA* get_rsa_from_evp_legacy(EVP_PKEY *pkey) { if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) { fprintf(stderr, "错误:非 RSA 密钥类型\n"); return NULL; } return pkey->pkey.rsa; // 直接访问结构体成员 } ``` #### 3. **统一兼容方案** ```c RSA* get_rsa_from_evp_unified(EVP_PKEY *pkey) { #if OPENSSL_VERSION_NUMBER < 0x10100000L // OpenSSL 1.0.x if (pkey->type != EVP_PKEY_RSA) return NULL; return pkey->pkey.rsa; #else // OpenSSL 1.1.0+ return EVP_PKEY_get1_RSA(pkey); #endif } ``` ### 使用示 ```c EVP_PKEY *pkey = load_key(); // 从文件/内存加载密钥 RSA *rsa = get_rsa_from_evp(pkey); if (rsa) { // 使用 RSA 结构进行操作(如获取模数) const BIGNUM *n, *e, *d; RSA_get0_key(rsa, &n, &e, &d); // 安全获取密钥参数 char *hex_n = BN_bn2hex(n); printf("RSA 模数 (n): %s\n", hex_n); OPENSSL_free(hex_n); // 使用传统 RSA 函数加密 unsigned char plain[32] = "Hello RSA"; unsigned char enc[256]; int enc_len = RSA_public_encrypt( sizeof(plain), plain, enc, rsa, RSA_PKCS1_OAEP_PADDING ); RSA_free(rsa); // 必须释放资源 } EVP_PKEY_free(pkey); ``` ### 关键说明 1. **引用计数管理**: - `EVP_PKEY_get1_RSA()` 会增加引用计数,使用后必须调用 `RSA_free()` - 直接访问 `pkey->pkey.rsa`(旧)不修改引用计数 - 错误处理需检查 OpenSSL 错误队列(`ERR_print_errors_fp`) 2. **密钥类型检查**: - 使用 `EVP_PKEY_id(pkey)` 获取密钥类型 - RSA 类型常量 `EVP_PKEY_RSA`(值为 6) 3. **安全获取密钥参数**: ```c const BIGNUM *n, *e, *d; RSA_get0_key(rsa, &n, &e, &d); // 只读访问 ``` 避免直接访问 `rsa->n` 等字段(OpenSSL 1.1.0+ 已封装) 4. **本兼容性**: - 使用 `OPENSSL_VERSION_NUMBER` 宏检测本 - 旧兼容方案需条件编译 ### 数学原理 RSA 密钥的数学表示: - **公钥**:$(n, e)$ - **私钥**:$(n, d)$ 其中: - $n = p \times q$(大素数乘积) - $e$ 是公钥指数(通常为 65537) - $d$ 是私钥指数,满足 $e \cdot d \equiv 1 \mod \phi(n)$ 加密过程:$c = m^e \mod n$ 解密过程:$m = c^d \mod n$[^2] ### 注意事项 1. **资源释放**: - 每个 `EVP_PKEY_get1_RSA()` 必须配对 `RSA_free()` - 原始 `EVP_PKEY` 需单独释放(`EVP_PKEY_free()`) 2. **密钥用途验证**: ```c if (RSA_check_key(rsa) != 1) { // 密钥无效处理 } ``` 3. **性能考虑**: - 频繁提取 RSA 结构会影响性能 - 优先使用 EVP API(如 `EVP_PKEY_encrypt()`) 4. **OpenSSL 3.0 迁移**: - 传统 RSA 函数标记为 "deprecated" - 建议改用 EVP API 实现完整操作[^1][^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值