小王的尴尬日常(一)--使用RSA公钥证书解密
来源:互联网 发布:nginx 转发效率 编辑:程序博客网 时间:2024/05/16 02:24
最近接了一个活,要用rsa的公钥解密,这个公钥存储在公钥证书里面,这个公钥证书呢… 安装在了windows操作系统里。以下是讲解部分,如果不想看的直接跳跃到最末尾的代码部分。
公钥–>公钥证书–>公钥证书库(Windows)
我要是使用它呢,就要反向过来:
解密<–提取公钥<–找到公钥证书<–打开公钥证书库(Windows)
当时我想这活没难度so easy, 可第一步就有点尴尬啊,先说这个公钥证书库的问题:
第一个证书库相关的函数,是CertOpenStore, 天下套路千千万,打开句柄占一半。无论是Windows还是linux这些个系统函数设计都是一个套路,遵循的树状结构。
HCERTSTORE WINAPI CertOpenStore(
__in LPCSTR lpszStoreProvider,
__in DWORD dwMsgAndCertEncodingType,
__in HCRYPTPROV_LEGACY hCryptProv,
__in DWORD dwFlags,
__in const void* pvPara
);
Instruction :The CertOpenStore function opens a certificate store by using a specified store provider type. While this function can open a certificate store for most purposes, CertOpenSystemStore is recommended to open the most common certificate stores. CertOpenStore is required for more complex options and special cases.
英文不好的兄弟自行脑补,反正就是打开一个证书库的操作句柄。
然后咱们仔细推敲一下这个函数,咱们先说第一个参数 lpszStoreProvider ,这个参数很重要,它决定了你以后的命运。
lpszStoreProvider
A pointer to a null-terminated ANSI string that specifies the store provider type.
就是选择一下你打开的证书库类型,它有很多种选择,常用的有以下几种:
CERT_STORE_PROV_MEMORY 内存里的证书库,程序里面创建用来在内存里存储证书
CERT_STORE_PROV_SYSTEM 系统里的证书库,存储在系统里面的证书库
CERT_STORE_PROV_FILENAME 文件里面的证书库,存储在文件里面的证书库
CERT_STORE_PROV_COLLECTION 证书库的集合,一般是程序里面创建用来添加其他证书库的
上面这四种是比较常用的,而我面临的情况是证书被安装在了系统里面,所以自然要用到 CERT_STORE_PROV_SYSTEM
咱们再说第二个参数 dwMsgAndCertEncodingType,这个就不仔细说了,证书编码格式,主要有以下两种:
PKCS_7_ASN_ENCODING
Specifies PKCS #7 message encoding.X509_ASN_ENCODING
Specifies X.509 certificate encoding
第三个参数 hCryptProv:
这个参数是让你选择csp,这个直接NULL就好了,默认就可以了。
第四个参数 dwFlags:
这个参数,结合第一个参数的CERT_STORE_PROV_SYSTEM能打出很多种套路,我就说两个常用的:
CERT_SYSTEM_STORE_LOCAL_MACHINE 本地系统证书库
CERT_SYSTEM_STORE_CURRENT_USER 当前用户证书库
这个选择完了还不算完,我们还有第五个参数 pvPara ,这个参数我们要指定证书的类型,我们知道在Windows下面证书划分为:个人、中级证书颁发机构、受信认的根证书颁发机构、受信任的发布者、其他人。但是这第五个参数怎么填?跟以上这几个类型都无关。
这是系统证书库的注册表存储结构,路径为:
HKEY_CURRENT_USER(或HKEY_LOCAL_MACHINE)\Software\Microsoft\SystemCertificates,比如说我的是中级证书颁发机构,存储的子键名为 CA,我就把‘CA’作为第五个参数。
HCERTSTORE hSysStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_A, // The store provider type 0, // The encoding type is NULL, // Use the default HCRYPTPROV CERT_SYSTEM_STORE_CURRENT_USER, // Set the store location CERT_SYSTEM_STORE_CURRENT_USER | CERT_SYSTEM_STORE_LOCAL_MACHINE "CA" // The store name as a Unicode "Root"|"TrustedPeople"|"TrustedPublisher" |"MY"|"TrustedPublisher" ))
接下来,我们要找到自己想要的那个证书,大家知道证书有很多的属性,比如序列号、有效日期、使用者、颁发者、算法、编码格式等等,我以其中颁发者作为key,来找到这个证书。网上有很多人说用CertFindCertificateInStore,而我选择了CertEnumCertificatesInStore,其实都一样。
PCCERT_CONTEXT pDesiredCert; while(pDesiredCert = CertEnumCertificatesInStore( hSysStore, pDesiredCert)) { CertGetNameString(pDesiredCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, issuser, MAX_PATH); printf("--%s\n", issuser); CertGetNameString(pDesiredCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, issuser, MAX_PATH); printf("--%s\n", issuser); if (!strcmp(ISSUSER, issuser)) { return (0); } }
让我尴尬的第一步完成了,接下来的事情更尴尬,本着一路怼到底的朴素思想,既然解析证书用的是WINAPI 那后面解密就用 CryptoAPI 。
CryptoAPI它神奇的地方在于不能直接用来加密解密,它加解密的步骤是这样的:
//先打开一个容器 CryptAcquireContext(....) //导入key或者产生Key CryptImportKey(.....) CryptGenKey(....)//用Key进行加密或解密CryptEncrypt(... ..)CryptDecrypt(... ..)
我乖乖的把以上步骤都做完了,更尴尬的事情出现了,解密的时候返回错误:0x8009000D 没有对应的Key选项,我查看了一下内存,有值啊,于是我开始网络搜索,最后再msdn的官网上找到了一个权威的解释,大体意思如下:
CryptEncrypt 解密的时候默认用私钥解密,当只导入公钥的时候就会报 0x8009000D 没有对应的Key的错误。
我这不r了大象了,我只用公钥啊,上哪给你找私钥去??当我懵逼了三秒之后想出了两种解决方案:
第一种,把公钥当做私钥导入到容器里面;
第二种,把公钥的参数解析出来,用openssl的算法去解密。
聪明机智的我最终选择了第二个方案….
openssl 里面有一个函数 RSA_public_decrypt ,这个完美解决我遇到的尴尬:
int RSA_public_decrypt( int flen, //n 的长度 const unsigned char *from, //密文数据 unsigned char *to, //解密数据 RSA *rsa, //RSA结构体 int padding); //是否填充
这里面别的参数还好说,最关键的就是这个RSA结构体,我们先看CryptoApi里面公钥结构
typedef struct _PUBLICKEYSTRUC { BYTE bType; BYTE bVersion; WORD reserved; ALG_ID aiKeyAlg;} BLOBHEADER, PUBLICKEYSTRUC;typedef struct _RSAPUBKEY { DWORD magic; // Has to be RSA1 DWORD bitlen; // # of bits in modulus DWORD pubexp; // public exponent // Modulus data follows} RSAPUBKEY;BYTE modulus[rsapubkey.bitlen/8];//以上为公钥结构,公私钥对还包含以下BYTE prime1[rsapubkey.bitlen/16];BYTE prime2[rsapubkey.bitlen/16];BYTE exponent1[rsapubkey.bitlen/16];BYTE exponent2[rsapubkey.bitlen/16];BYTE coefficient[rsapubkey.bitlen/16];BYTE privateExponent[rsapubkey.bitlen/8];
我们再看一下openssl 里面RSA的结构体:
struct rsa_st { /* The first parameter is used to pickup errors where * this is passed instead of aEVP_PKEY, it is set to 0 */ int pad; long version; const RSA_METHOD *meth; /* functional reference if 'meth' is ENGINE-provided */ ENGINE *engine; BIGNUM *n; BIGNUM *e; BIGNUM *d; BIGNUM *p; BIGNUM *q; BIGNUM *dmp1; BIGNUM *dmq1; BIGNUM *iqmp; /* be careful using this if the RSA structure is shared */ CRYPTO_EX_DATA ex_data; int references; int flags; /* Used to cache montgomery values */ BN_MONT_CTX *_method_mod_n; BN_MONT_CTX *_method_mod_p; BN_MONT_CTX *_method_mod_q; /* all BIGNUM values are actually in the following data, if it is not * NULL */ char *bignum_data; BN_BLINDING *blinding; BN_BLINDING *mt_blinding; };
里面进行公钥解密时必须的参数为:
const RSA_METHOD *meth;
BIGNUM *n;
BIGNUM *e;
第一个参数是rsa加解密时要用到函数的地址,n和e是我们需要填充的:
RSA.n 对应的是 BYTE modulus[rsapubkey.bitlen/8];
RSA.e 对应的是 RSAPUBKEY.pubexp
下面是完整的代码:
#include<openssl/rsa.h>#include<openssl/pem.h>#include<openssl/err.h>#define ISSUSER #define CONTIANER #define BUFSIZE 4096HCERTSTORE hSysStore;PCCERT_CONTEXT pDesiredCert; HCRYPTPROV hTmpProv;HCRYPTKEY hKey;HANDLE hStoreFileHandle; char lpPathBuffer[MAX_PATH];char szTempName[MAX_PATH]; BYTE Rsa_n[BUFSIZE] = {0};RSA* pRSAPubKey;BN_ULONG Rsa_e;//ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/seccrypto/security/base_provider_key_blobs.htm//遍历找到公钥证书int GetCerContext(){ if(hSysStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_A, // The store provider type 0, // The encoding type is NULL, // Use the default HCRYPTPROV CERT_SYSTEM_STORE_CURRENT_USER, // Set the store location CERT_SYSTEM_STORE_CURRENT_USER | CERT_SYSTEM_STORE_LOCAL_MACHINE "CA" // The store name as a Unicode "Root"|"TrustedPeople"|"TrustedPublisher" |"MY"|"TrustedPublisher" )) { printf("The system store was created successfully.\n"); } else { printf("An error occurred during creation " "of the system store \n"); return GetLastError(); } char issuser[MAX_PATH] = {0}; while(pDesiredCert = CertEnumCertificatesInStore( hSysStore, pDesiredCert)) { CertGetNameString(pDesiredCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, issuser, MAX_PATH); printf("--%s\n", issuser); CertGetNameString(pDesiredCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, issuser, MAX_PATH); printf("--%s\n", issuser); if (!strcmp(ISSUSER, issuser)) { return (0); } } return GetLastError();}//将公钥导入容器int ImportKey(){ bool bRet = CryptAcquireContext(&hTmpProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET); // NULL表示使用系统默认CSP DWORD dwError = GetLastError(); if (dwError != 0x8009000f && dwError != 0x00) { return dwError; } bRet = CryptAcquireContext(&hTmpProv, CONTIANER, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET); // NULL表示使用系统默认CSP if (!bRet) { return GetLastError(); } bRet = CryptImportPublicKeyInfo(hTmpProv, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, &pDesiredCert->pCertInfo->SubjectPublicKeyInfo, &hKey); if (!bRet) { return GetLastError(); } return 0;}int GetRSAPubKey(){ DWORD cbPubKeyBlob = BUFSIZE; PUBLICKEYSTRUC sPubKey; RSAPUBKEY sRSAPubKey; DWORD dwOffset = 0; BYTE PubKeyBlob[BUFSIZE] = {0}; BOOL bRet = CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, PubKeyBlob, &cbPubKeyBlob); if (!bRet) { return GetLastError(); } memcpy(&sPubKey, &PubKeyBlob[dwOffset], sizeof(PUBLICKEYSTRUC)); dwOffset += sizeof(PUBLICKEYSTRUC); memcpy(&sRSAPubKey, &PubKeyBlob[dwOffset], sizeof(RSAPUBKEY)); dwOffset += sizeof(RSAPUBKEY); memcpy(Rsa_n, &PubKeyBlob[dwOffset], sRSAPubKey.bitlen/8); pRSAPubKey = RSA_new(); pRSAPubKey->n = BN_new(); pRSAPubKey->e = BN_new(); pRSAPubKey->n->d = (BN_ULONG*)(&Rsa_n[0]); pRSAPubKey->n->top = sRSAPubKey.bitlen/32 ; pRSAPubKey->n->dmax = pRSAPubKey->n->top + 1; pRSAPubKey->n->flags = 1; pRSAPubKey->n->neg = 0; Rsa_e = sRSAPubKey.pubexp; BN_set_word(pRSAPubKey->e, Rsa_e); return 0;}//解密hash值int DeCryptHash(char* pEnCryptData, char* pDestData){ RSA *p_rsa; int rsa_len = RSA_size(pRSAPubKey); RSA_public_decrypt(rsa_len, pEnCryptData, pDestData, pRSAPubKey, RSA_NO_PADDING); //NTE_BAD_FLAGS return 0;}//释放资源void Rlease(){ if (hKey) { CryptDestroyKey(hKey); } if (hTmpProv) { CryptReleaseContext(hTmpProv, 0); } if(pDesiredCert) { CertFreeCertificateContext(pDesiredCert); } if(hSysStore) { CertCloseStore(hSysStore, CERT_CLOSE_STORE_CHECK_FLAG); } if (hStoreFileHandle != NULL && hStoreFileHandle != INVALID_HANDLE_VALUE) { CloseHandle(hStoreFileHandle); } return;}int _tmain(int argc, _TCHAR* argv[]){ //GetCerContext(); //SaveCerfication(); GetCerContext(); ImportKey(); GetRSAPubKey(); DeCryptHash(...., ....); Rlease(); return 0;}
- 小王的尴尬日常(一)--使用RSA公钥证书解密
- 小王的尴尬日常(三)--Openssl 实现国密算法(加密和解密)
- 小王的尴尬日常(二)---Openssl 实现国密算法(基础介绍和产生秘钥对)
- 小王的尴尬日常(四)--openssl 实现国密算法(签名和验签)
- C#使用RSA证书文件加密和解密示例(任意长度的内容)
- RSA公钥解密
- 使用X.509数字证书加密解密实务(二)-- 使用RSA证书加密敏感数据
- 使用X.509数字证书加密解密实务(二)-- 使用RSA证书加密敏感数据
- 使用X.509数字证书加密解密实务(二)-- 使用RSA证书加密敏感数据
- 使用X.509数字证书加密解密实务(二)-- 使用RSA证书
- RSA加密解密的使用!
- RSA 公钥解密的一些心得!
- C#使用RSA证书文件加密和解密示例
- RSA加密解密及证书
- 小王梦游记(一)
- 使用X.509数字证书加密解密实务(一)-- 证书的获得和管理
- 使用X.509数字证书加密解密实务(一)-- 证书的获得和管理
- 使用X.509数字证书加密解密实务(一)-- 证书的获得和管理
- 单例模式简单原理
- S3C2440之裸板GPIO操作
- JAVA几种常用容器
- Photoshop操作说明——初学篇
- font-face使用阿里图标库
- 小王的尴尬日常(一)--使用RSA公钥证书解密
- windows下运行webLogic startWeblogic.bat等命令报错
- 【jzoj3861】【JSOI2014】【支线剧情2 】【树形动态规划】
- 长文本如何兼顾效率情况下过滤垃圾信息
- Qt 之 安装及调试源码
- 项目周期管理工具maven常用命令
- 解决Jpush大批量循环推送失败
- C++指针与引用的区别
- 删除项目中的CocoaPods