基本加密函数
基本加密函数为开发加密应用程序提供了足够灵活的空间。所有CSP的通讯都是通过这些函数。一个CSP是实现所有加密操作的独立模块。在每一个应用程序中至少需要提供一个CSP来完成所需的加密操作。如果使用多于一个以上的CSP,在加密函数调用中就要指定所需的CSP。微软基本加密提供者(Microsoft Base Cryptographic Provider),是缺省绑定到CryptoAPI 里的。如果没有指定其他CSP时,这个CSP 就是却省的。每一个CSP对CryptoAPI 提供了一套不同的实现。一些提供了更加强大的加密算法,而其他一些CSP包含了对硬件的支持,比如智能卡。另外,一些CSP 偶尔和使用者直接通讯,比如数字签名就使用了用户的签名私钥。
基本加密函数包含了以下几种:
1.1服务提供者函数
应用程序使用服务提供者函数来连接和断开一个CSP。下面就是主要的API:
CryptAcquireContext获得指定CSP的密钥容器的句柄
CryptContextAddRef对HCRYPTPROV句柄增加一个应用计数
CryptEnumProviders枚举当前计算机中的CSP
CryptEnumProviderTypes枚举CSP的类型
CryptGetDefaultProvider对于指定CSP类型的却省CSP
CryptGetProvParam得到一个CSP的属性
CryptInstallDefaultContext安装先前得到的HCRYPTPROV上下文作为当前却省的上下文
CryptReleaseContext释放由CryptAcquireContext得到的句柄
CryptSetProvider和CryptSetProviderEx为指定CSP类型指定一个却省的CSP
CryptSetProvParam指定一个CSP的属性
CryptUninstallDefaultContext删除先前由CryptInstallDefaultContext安装的却省上下文
1.2密钥的产生和交换函数
密钥产生函数创建、配置和销毁加密密钥。他们也用于和其他用户进行交换密钥。下面就是主要的一些函数:
CryptAcquireCertificatePrivateKey对于指定证书上下文得到一个HCRYPTPROV句柄和dwKeySpec
CryptDeriveKey从一个密码中派生一个密钥
CryptDestoryKey销毁密钥
CryptDuplicateKey制作一个密钥和密钥状态的精确复制
CryptExportKey把CSP的密钥做成BLOB 传送到应用程序的内存空间中
CryptGenKey创建一个随机密钥
CryptGenRandom产生一个随机数
CryptGetKeyParam得到密钥的参数
CryptGetUserKey得到一个密钥交换或签名密钥的句柄
CryptImportKey把一个密钥BLOB传送到CSP 中
CryptSetKeyParam指定一个密钥的参数
1.3编码/解码函数
有一些编码/解码函数,他们可以用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码。
以下就是这几个函数:
CryptDecodeObject对lpszStructType结构进行解码
CryptDecodeObjectEx对lpszStructType结构进行解码,此函数支持内存分配选项
CryptEncodeObject对lpszStructType结构进行编码
CyptEncodeObjectEx对lpszStructType结构进行编码,此函数支持内存分配选项
1.4数据加密/解密函数
这些函数支持数据的加密/解密操作。
CryptEncrypt 和CryptDecrypt 要求在被调用前指定一个密钥。
注:这个密钥可以由CryptGenKey、CryptDeriveKey 或CryptImportKey 产生。创建密钥时要指定加密算法。
CryptSetKeyParam函数可以指定额外的加密参数。
CryptDecrypt使用指定加密密钥来解密一段密文
CryptEncrypt使用指定加密密钥来加密一段明文
CryptProtectData执行对DATA_BLOB结构的加密
CryptUnprotectData执行对DATA_BLOB结构的完整性验证和解密
1.5哈希和数字签名函数
这些函数在应用程序中完成计算哈希、创建和校验数字签名。
CryptCreateHash创建一个空哈希对象
CryptDestoryHash销毁一个哈希对象
CryptDuplicateHash复制一个哈希对象
CryptGetHashParam得到一个哈希对象参数
CryptHashData对一块数据进行哈希,把它加到指定的哈希对象中
CryptHashSessionKey对一个会话密钥进行哈希,把它加到指定的哈希对象中
CryptSetHashParam设置一个哈希对象的参数
CryptSignHash对一个哈希对象进行签名
CryptVerifySignature校验一个数字签名
1.6函数详解
1.6.1获得CSP密钥容器句柄
1.6.1.1 CryptAcquireContext
BOOL WINAPI CryptAcquireContext(
HCRYPTPROV *phProv,
LPCTSTR pszContainer,
LPCTSTR pszProvider,
DWORD dwProvType,
DWORD dwFlags
);
参数:
phProv[out] CSP句柄指针
pszContainer[in]密钥容器名称, 指向密钥容器的字符串指针。如果dwFlags为CRYPT_VERIFYCONTEXT,pszContainer必须为NULL。
pszProvider[in]指向CSP名称的字符串指针。如果为NULL,就使用却省的CSP。
dwProvType[in]CSP类型。下表是就中常见的CSP类型
CSP类型 |
交换算法 |
签名算法 |
对称加密算法 |
Hash算法 |
PROV_RSA_FULL |
RSA |
RSA |
RC2 |
MD5 |
PROV_RSA_SIG |
none |
RSA |
none |
MD5 |
PROV_RSA_SCHANNEL |
RSA |
RSA |
RC4 |
MD5 |
PROV_DSS |
DSS |
none |
DSS |
MD5 |
PROV_DSS_DH |
DH |
DSS |
CYLINK_MEK |
MD5 |
PROV_DH_SCHANNEL |
DH |
DSS |
DES |
MD5 |
PROV_FORTEZZA |
KEA |
DSS |
Skipjack |
SHA |
PROV_MS_EXCHANGE |
RSA |
RSA |
CAST |
MD5 |
PROV_SSL |
RSA |
RSA |
Varies |
Varies |
dwFlags[in]标志:
CRYPT_VERIFYCONTEXT |
指出应用程序不需要使用公钥/私钥对,如程序只执行哈希和对称加密。只有程序需要创建签名和解密消息时才需要访问私钥。 |
CRYPT_NEWKEYSET |
使用指定的密钥容器名称创建一个新的密钥容器。如果pszContainer为NULL,密钥容器就使用却省的名称创建。 |
CRYPT_MACHINE_KEYSET |
由此标志创建的密钥容器只能由创建者本人或有系统管理员身份的人使用。 |
CRYPT_DELETEKEYSET |
删除由pszContainer指定的密钥容器。如果pszContainer 为NULL,缺省名称的容器就会被删除。此容器里的所有密钥对也会被删除。 |
CRYPT_SLIENT |
应用程序要求CSP不显示任何用户界面。 |
说明:
这个函数是用来取得指定CSP密钥容器句柄,以后的任何加密操作就是针对此CSP 句柄而言。函数首先查找由dwProvType和pszProvider 指定的CSP,如果找到了CSP,函数就查找由此CSP指定的密钥容器。由适当的dwFlags 标志,这个函数就可以创建和销毁密钥容器,如果不要求访问私钥的话,也可以提供对CSP临时密钥容器的访问。
1.6.1.2 CryptReleaseContext
BOOL WINAPI CryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlags
);
参数:
hProv[in]由CryptAcquireContext获得的CSP 句柄。
dwFlags[in]保留。必须为0。
说明:
此函数释放CSP的句柄。对于每一次调用,CSP 的引用计数都减1。当引用计数为0时,CSP上下文就会被系统释放变成无效句柄,以后针对此CSP 句柄的函数不再可用。
此函数并不销毁密钥容器或密钥对。
//--------------------------------------------------------------------
HCRYPTPROV hCryptProv;
if (CryptAcquireContext(
hCryptProv, NULL,
MS_DEF_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
CryptReleaseContext(hCryptProv, NULL);
1.6.2枚举CSP
4.1.6.2.1 CryptEnumProviders
BOOL WINAPI CryptEnumProviders(
DWORD dwIndex,
DWORD *pdwReserved,
DWORD dwFlags,
DWORD *pdwProvType,
LPTSTR pszProvName,
DWORD *pcbProvName
);
参数:
dwIndex[in]枚举下一个CSP的索引。
pdwReserved[in]保留。必须为NULL。
dwFlags[in]保留。必须为NULL。
pdwProvType[out]CSP的类型。
pszProvName[out]指向接收CSP名称的缓冲区字符串指针。此指针可为NULL,用来得到字符串的大小。
pcbProvName[in/out]指出pszProvName字符串的大小。
说明:
此函数得到第一个或下一个可用的CSP。如果使用循环,就可以得到计算机上所有可用的CSP。
1.6.3获得CSP参数
4.1.6.3.1 CryptGetProvParam
BOOL WINAPI CryptGetProvParam(
HCRYPTPROV hProv,
DWORD dwParam,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwFlags
);
参数:
hProv[in]CSP句柄。
dwParam[in]指定查询的参数。
参数名 |
作用 |
PP_CONTAINER |
指向密钥名称的字符串 |
PP_ENUMALGS |
不断的读出CSP支持的所有算法 |
PP_ENUMALGS_EX |
比PP_ENUMALGS获得更多的算法信息 |
PP_ENUMCONTAINERS |
不断的读出CSP支持的密钥容器 |
PP_IMPTYPE |
指出CSP怎样实现的 |
PP_NAME |
指向CSP名称的字符串 |
PP_VERSION |
CSP的版本号 |
PP_KEYSIZE_INC |
AT_SIGNATURE的位数 |
PP_KEYX_KEYSIZE_INC |
AT_KEYEXCHANGE的位数 |
PP_KEYSET_SEC_DESCR |
密钥的安全描述符 |
PP_UNIQUE_CONTAINER |
当前密钥容器的唯一名称 |
PP_PROVTYPE |
CSP类型 |
PP_USE_HARDWARE_RNG |
指出硬件是否支持随机数发生器 |
PP_KEYSPEC |
返回CSP密钥的信息 |
pbData[out]指向接收数据的缓冲区指针。
pdwDataLen[in/out]指出pbData数据长度。
dwFlags[in]如果指定PP_ENUMCONTAINERS,就指定CRYPT_MACHINE_KEYSET。
说明:
此函数获得CSP的各种参数。
//----------------------------------------------------------------- // HCRYPTPROV hCryptProv; BYTE pbData[1000]; DWORD cbData; //------------------------------------------------------------------- //缺省CSP名称 cbData = 1000; if(CryptGetProvParam( hCryptProv, PP_NAME, pbData, &cbData, 0)) { printf("CryptGetProvParam succeeded.\n"); printf("Provider name: %s\n", pbData); } else { printf("Error reading CSP name. \n"); exit(1); } //-------------------------------------------------------------------- cbData = 1000; if(CryptGetProvParam( hCryptProv, PP_CONTAINER, pbData, &cbData, 0)) { printf("CryptGetProvParam succeeded. \n"); printf("Key Container name: %s\n", pbData); } else { printf("Error reading key container name. \n"); exit(1); }
4.1.6.4创建哈希
4.1.6.4.1 CryptCreateHash
BOOL WINAPI CryptCreateHash(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTKEY hKey,
DWORD dwFlags,
HCRYPTHASH *phHash
);
参数:
hProv[in]CSP句柄
Algid[in]哈希算法的标示符。
hKey[in]如果哈希算法是密钥哈希,如HMAC或MAC 算法,就用此密钥句柄传递密钥。对于非密钥算法,此参数为NULL。
dwFlags[in]保留。必须为0。
phHash[out]哈希对象的句柄。
说明:
此函数初始化哈希数据流。它创建并返回了一个CSP哈希对象的句柄。此句柄由CryptHashData和CryptHashSessionKey来调用。
4.1.6.4.2 CryptHashData
BOOL WINAPI CryptHashData(
HCRYPTHASH hHash,
BYTE *pbData,
DWORD dwDataLen,
DWORD dwFlags
);
参数:
hHash[in]哈希对象句柄
pbData[in]指向要加入到哈希对象的数据指针
dwDataLen[in]数据长度
dwFlags[in]标志
CRYPT_USERDATA所有微软CSP都忽略此参数。所有其他CSP 都不能忽略此参数,如果置此参数,CSP提示用户直接数据数据。
说明:
此函数把一段数据加入到指定的哈希对象中去。
4.1.6.4.3 CryptGetHashParam
BOOL WINAPI CryptGetHashParam(
HCRYPTHASH hHash,
DWORD dwParam,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwFlags
);
参数:
hHash[in]哈希对象的句柄
dwParam[in]查询类型。可以是下列:
参数名称 |
作用 |
HP_ALGID |
哈希算法 |
HP_HASHSIZE |
哈希值长度 |
HP_HASHVAL |
哈希值,由hHash指定的哈希值或者消息哈希 |
说明:
此函数得到指定哈希对象的数据。
4.1.6.4.4 CryptDestroyHash
BOOL WINAPI CryptDestroyHash(
HCRYPTHASH hHash
);
参数:
hHash[in]要销毁的哈希对象句柄
说明:
此函数销毁由hHash指定的哈希对象。当一个哈希对象被销毁后,它对程序来说不可用。
… HCRYPTHASH hCryptHash; if (CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hCryptHash )) CryptDestroyHash(hCryptHash); …
4.1.6.5派生密钥
4.1.6.5.1 CryptDeriveKey
BOOL WINAPI CryptDeriveKey(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTHASH hBaseData,
DWORD dwFlags,
HCRYPTKEY *phKey
);
参数:
hProv[in]CSP句柄
Algid[in]要产生密钥的对称加密算法
hBaseData[in]哈希对象的句柄
dwFlags[in]指定密钥的类型
参数 |
作用 |
CRYPT_CREATE_SALT |
由哈希值产生一个会话密钥,有一些需要补位。如果用此标志,密钥将会赋予一个盐值 |
CRYPT_EXPORTABLE |
如果置此标志,密钥就可以用CryptExportKey函数导出。 |
CRYPT_NO_SALT |
如果置此标志,表示40位的密钥不需要分配盐值。 |
CRYPT_UPDATE_KEY |
有些CSP从多个哈希值中派生会话密钥。如果这种情况,CryptDeriveKey需要多次调用。 |
phKey[in/out]密钥的句柄
说明:
此函数从一基本数据值中派生会话密钥。函数保证当CSP和算法相同时,从相同基本数据值中产生的密钥是唯一的。
4.1.6.5.2 CryptDestroyKey
BOOL WINAPI CryptDestroyKey(
HCRYPTKEY hKey
);
参数:
hKey[in]需要销毁的密钥句柄
说明:
此函数释放密钥句柄。
… HCRYPTKEY hCryptKey; if (CryptDeriveKey(hCryptProv, m_algid, m_hHash, 0, &hCryptKey)) CryptDestroyKey(hCryptKey); …
4.1.6.6加密/解密
4.1.6.6.1 CryptEncrypt
BOOL WINAPI CryptEncrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwBufLen
);
参数:
hKey[in]加密密钥的句柄
hHash[in]哈希对象的句柄。如果数据需要同时被哈希并且加密,hHash就指出了哈希对象。
Final[in]指出是否是最后一次加密操作。如果Final为TRUE,就为最后一次,否则为FALSE。
dwFlags[in]保留
pbData[in/out]指向被加密的数据地址。
pdwDataLen[in/out]指向一个DWORD值的地址。在调用此函数前,这个值就是需要加密的数据长度。在调用此函数后,这个值就是已经加密的数据长度。如果此值为NULL,函数就返回需要数据的长度。
dwBufferLen[in]指出pbData的数据长度。
说明:
此函数用于加密数据。加密数据所需要的算法由hKey的密钥指定。
4.1.6.6.2 CryptDecrypt
BOOL WINAPI CryptDecrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen
);
参数:
hKey[in]解密密钥的句柄
hHash[in]哈希对象的句柄。如果需要解密数据并且同时作哈希,hHash传递此参数。
Final[in]指出是否是最后一次解密操作。
dwFlags[in]保留
pbData[in/out]需要解密数据的地址
pdwDataLen[in/out]指向DWORD值的指针,此值指出解密数据的长度。在调用此函数前,此值为需要解密数据的长度,调用此函数后,此值为已经解密的数据长度。
说明:
此函数对由CryptEncrypt加密过的数据进行解密。
//--------------------------------------------------- HCRYPTPROV hCryptProv; HCRYPTHASH hCryptHash; HCRYPTKEY hCryptKey; CryptAcquireContext( hCryptProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hCryptHash ); static char szHash[]=”PISAHASHDATA”; //原始字符串 DWORD dwLen=strlen(szHash); CryptHashData(hCryptHash, (BYTE*)szHash, dwLen, 0); CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0, &hCryptKey); static char szEntry[]=“PISA2002”; DWORD dwLenIn = strlen(szEntry); DWORD dwLenOut=dwLenIn; CryptEncrypt(hCryptKey, 0, TRUE, 0, (BYTE*)szEntry, &dwLenOut, dwLenIn); CryptDecrypt(hCryptKey, 0, TRUE, 0,(BYTE*)szEntry,&dwLenOut); CryptDestroyKey(hCryptKey); CryptDestroyHash(hCryptHash); CryptReleaseContext(hCryptProv, NULL);
4.1.6.7签名/验证
4.1.6.7.1 CryptSignMessage
BOOL WINAPI CryptSignMessage(
PCRYPT_SIGN_MESSAGE_PARA pSignPara,
BOOL fDetachedSignature,
DWORD cToBeSigned,
const BYTE *rgpbToBeSigned[ ],
DWORD rgcbToBeSigned[ ],
BYTE *pbSignedBlob,
DWORD *pcbSignedBlob
);
参数:
pSignPara[in]指向CRYPT_SIGN_MESSAGE_PARA结构的指针。
CRYPT_SIGN_MESSAGE_PARA结构如下:
typedef struct _CRYPT_SIGN_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; PCCERT_CONTEXT pSigningCert; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; void *pvHashAuxInfo; DWORD cMsgCert; PCCERT_CONTEXT *rgpMsgCert; DWORD cMsgCrl; PCCRL_CONTEXT *rgpMsgCrl; DWORD cAuthAttr; PCRYPT_ATTRIBUTE rgAuthAttr; DWORD cUnauthAttr; PCRYPT_ATTRIBUTE rgUnauthAttr; DWORD dwFlags; DWORD dwInnerContentType; #ifdef CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS CRYPT_ALGORITHM_IDNETIFIER HashEncryptionAlgorithm; void pvHashEncryptionAuxInfo; #endif } CRYPT_SIGN_MESSAGE_PARA, *PCRYPT_SIGN_MESSAGE_PARA;
cbSize此结构的大小。
dwMsgEncodingType使用的编码类型。一般为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
pSigningCert指向要签名的CERT_CONTEXT指针。
HashAlgorithm CRYPT_ALGORITHM_IDENTIFIER指出了对要进行签名的数据进行哈希的哈希算法
pvHashAuxInfo必须为NULL
cMsgCert rgpMsgCert数组中CERT_CONTEXT结构的元素数量。如果此值为0,则签名消息中不包含任何证书。
rgpMsgCert指向CERT_CONTEXT的数组指针。如果包含pSigningCert,它的指针必须放到rgpMsgCert数组中。
cMsgCrl rgpMsgCrl数组指向CRL_CONTEXT结构的元素数量。如果为0,签名消息中不包含任何CRL_CONTEXT结构。
rgpMsgCrl 指向CRL_CONTEXT结构的数组指针。
cAuthAttr必须为0
rgAuthAttr指向CRYPT_ATTRIBUTE数组的指针。每一次都包含了认证信息。
cUnauthAttrrg UnauthAttr数组大小。
rgUnauthAttr指向CRYPT_ATTRIBUTE结构的数组指针。
dwFlags通常为0。
dwInnerContentType通常为0。
HashEncryptionAlgorithm CRYPT_ALGORITHM_IDENTIFIER结构。通常为0
pvHashEncryptionAuxInfo必须为0。
fDetachedSignature[in]如果为TRUE,就是已解邦定的签名,否则为FALSE。如果此参数为TRUE,
pbSignedBlob中只有签名哈希。否则rgpbToBeSigned和签名哈希都要被编码。
cToBeSigned[in]指出rgpbToBeSigned数据元素的个数。除非fDetachedSigned 为TRUE,此参数就是1。
rgpbToBeSigned[in]指向要签名数据的数组指针。
pbSignedBlob[out]指向一个接收签名哈希的数据地址。如果此参数为NULL,就是需要来接收数据的内存大小。
pcbSignedBlob[in/out]指向DWORD的地址,此数据指出pbSignedBlob 的大小。
说明:
此函数对指定数据进行哈希,然后对哈希值进行签名,然后对原始消息和签名哈希进行编码。
//-------------------------------------------------------------------- … #define MY_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) #define SIGNER_NAME L"Insert_signer_name_here" #define CERT_STORE_NAME L"MY" void HandleError(char *s); void main(void) { //系统证书库句柄 HCERTSTORE hStoreHandle; //-------------------------------------------------------------------- //待签名的消息 BYTE* pbMessage = (BYTE*)"CryptoAPI is a good way to handle security"; //-------------------------------------------------------------------- DWORD cbMessage = strlen((char*) pbMessage)+1; //-------------------------------------------------------------------- //证书的上下文 PCCERT_CONTEXT pSignerCert; CRYPT_SIGN_MESSAGE_PARA SigParams; DWORD cbSignedMessageBlob; BYTE *pbSignedMessageBlob; DWORD cbDecodedMessageBlob; BYTE *pbDecodedMessageBlob; CRYPT_VERIFY_MESSAGE_PARA VerifyParams; //-------------------------------------------------------------------- const BYTE* MessageArray[] = {pbMessage}; DWORD MessageSizeArray[1]; MessageSizeArray[0] = cbMessage; //-------------------------------------------------------------------- // printf("Begin processing. \n"); printf(" The message to be signed is\n-> %s.\n",pbMessage); //-------------------------------------------------------------------- // Open a certificate store. if ( !( hStoreHandle = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, CERT_STORE_NAME))) { HandleError("The MY store could not be opened."); } //-------------------------------------------------------------------- // //得到证书的上下文,此证书必须能访问签名者的私钥 if(pSignerCert = CertFindCertificateInStore( hStoreHandle, MY_TYPE, 0, CERT_FIND_SUBJECT_STR, SIGNER_NAME, NULL)) { printf("The signer's certificate was found.\n"); } else { HandleError( "Signer certificate not found."); } //-------------------------------------------------------------------- //初始化签名结构 SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA); SigParams.dwMsgEncodingType = MY_TYPE; SigParams.pSigningCert = pSignerCert; SigParams.HashAlgorithm.pszObjId = szOID_RSA_MD5; SigParams.HashAlgorithm.Parameters.cbData = NULL; SigParams.cMsgCert = 1; SigParams.rgpMsgCert = &pSignerCert; SigParams.cAuthAttr = 0; SigParams.dwInnerContentType = 0; SigParams.cMsgCrl = 0; SigParams.cUnauthAttr = 0; SigParams.dwFlags = 0; SigParams.pvHashAuxInfo = NULL; SigParams.rgAuthAttr = NULL; //-------------------------------------------------------------------- // //首先得到BLOB的大小 if(CryptSignMessage( &SigParams, // Signature parameters FALSE, // Not detached 1, // Number of messages MessageArray, // Messages to be signed MessageSizeArray, // Size of messages NULL, // Buffer for signed message &cbSignedMessageBlob)) // Size of buffer { printf("The size of the BLOB is %d.\n",cbSignedMessageBlob); } else { HandleError("Getting signed BLOB size failed"); } //-------------------------------------------------------------------- //分配BLOB的内存. if(!(pbSignedMessageBlob = (BYTE*)malloc(cbSignedMessageBlob))) { HandleError("Memory allocation error while signing."); } //-------------------------------------------------------------------- // if(CryptSignMessage( &SigParams, // FALSE, // 1, //消息数量 MessageArray, //待签名的消息 MessageSizeArray, //消息大小 pbSignedMessageBlob, //缓冲区 &cbSignedMessageBlob)) //缓冲区大小 { printf("The message was signed successfully. \n"); } else { HandleError("Error getting signed BLOB"); } //-------------------------------------------------------------------- //验证签名信息 //-------------------------------------------------------------------- //初始化VerifyParams结构. VerifyParams.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA); VerifyParams.dwMsgAndCertEncodingType = MY_TYPE; VerifyParams.hCryptProv = 0; VerifyParams.pfnGetSignerCertificate = NULL; VerifyParams.pvGetArg = NULL; //-------------------------------------------------------------------- // if(CryptVerifyMessageSignature( &VerifyParams, //. 0, // pbSignedMessageBlob, //. cbSignedMessageBlob, // NULL, // &cbDecodedMessageBlob, //. NULL)) // Pointer to signer certificate. { printf("%d bytes need for the buffer.\n",cbDecodedMessageBlob); } else { printf("Verification message failed. \n"); } //-------------------------------------------------------------------- //为缓冲区分配内存. if(!(pbDecodedMessageBlob = (BYTE*)malloc(cbDecodedMessageBlob))) { HandleError("Memory allocation error allocating decode BLOB."); } //-------------------------------------------------------------------- // //得到缓冲区的大小 if(CryptVerifyMessageSignature( &VerifyParams, // Verify parameters. 0, // Signer index. pbSignedMessageBlob, // Pointer to signed BLOB. cbSignedMessageBlob, // Size of signed BLOB. pbDecodedMessageBlob, // Buffer for decoded message. &cbDecodedMessageBlob, // Size of buffer. NULL)) // Pointer to signer certificate. { printf("The verified message is \n-> %s \n",pbDecodedMessageBlob); } else { printf("Verification message failed. \n"); } //-------------------------------------------------------------------- // if(pbSignedMessageBlob) free(pbSignedMessageBlob); if(pbDecodedMessageBlob) free(pbDecodedMessageBlob); if(pSignerCert) CertFreeCertificateContext(pSignerCert); if(CertCloseStore( hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG)) { printf("The store closed and all certificates are freed. \n"); } else { printf("Store closed after signing -- \n" "not all certificates, CRLs or CTLs were freed"); } …
4.2证书和证书库函数
这组函数管理、使用和取得证书、证书撤销列表和证书信任列表。这些函数可以分成一下几组:
4.2.1证书库函数
一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了
对库获得、枚举、验证和使用证书库里的信息。
以下就是这些函数:
CertAddStoreToCollection在证书库中增加一个证书
CertCloseStore关闭一个证书库句柄
CertControlStore如果证书缓冲区和证书本身内容不相符时,允许给应用程序发一个通知
CertDuplicateStore通过增加引用计数来复制证书库句柄
CertEnumPhysicalStore对于指定系统库枚举物理库
CertEnumSystemStore枚举所有可用的系统库
CertEnumSystemStoreLocation枚举可用系统库的所有位置
CertGetStoreProperty得到一个库的属性
CertOpenStore使用指定库类型来打开证书库
CertOpenSystemStore打开一个系统证书库
CertRegisterPhysicalStore在一个注册系统库里增加一个物理库
CertRegisterSystemStore注册一个系统库
CertRemoveStoreFromCollection从一个库集合里删除证书库
CertSaveStore保存证书库
CertSetStoreProperty设置证书属性
CertUnregisterPhysicalStore从系统库中删除一个物理库
CertUnregisterSystemStore反注册一个指定系统库
4.2.2维护函数
CryptoAPI提供了证书和证书库函数如下:
CertAddSerializeElementToStore在库中增加一系列证书或CRL
CertCreateContext从编码字节中创建指定上下文
CertEnumSubjectInSortedCTL在CTL库中枚举信任主题
CertFindSubjectInCTL在CTL中寻找指定主题
CertFindSubjectInSortedCTL在分类CTL中寻找指定主题
4.2.3证书函数
下列函数是针对于证书的。大多数函数都是处理CRL和CTL 的。
CertAddCertificateContextToStore在证书库里增加一个证书上下文
CertAddCertificateLinkToStore在证书库里增加一个对不同库里的证书上下文的链接
CertAddEncodedCertificateToStore把编码证书转换成证书上下文并且把它加到证书库里
CertCreateCertificateContext从编码证书中创建一个证书上下文。但这个上下文并不放到证书库里
CertCreateSelfSignCertificate创建一个自签名证书
CertDeleteCertificateFromStore从证书库里删除一个证书
CertDuplicateCertificate通过增加引用计数来复制证书上下文
CertEnumCertificateInStore在证书库里枚举证书上下文
CertFindCertificateInStore在证书库里寻找证书上下文
CertFreeCertificateContext释放一个证书上下文
CertGetIssuerCertificateFromStore在证书库里得到指定主题证书的发行者
CertGetSubjectCertificateFromStore获得主题证书的上下文
CertGetValidUsages返回所有证书的用法
CertSerializeCertificateStoreElement串行化编码证书的证书上下文
CertVerifySubjectCertificateContext使用发行者来验证主题证书
CryptUIDlgViewContext显示证书、CRL或CTL
CryptUIDlgSelectCertificateFromStore从指定库中显示对话框,可以从中选择证书
4.2.4证书撤销列表函数
CertAddCRLContextToStore在证书库里增加一个CRL上下文
CertAddCRLLinkToStore在不同的库里增加一个CRL上下文链接
CertAddEncodedCRLToStore把编码CRL转化成CRL 上下文然后把它加入到证书库中
CertCreateCRLContext从编码CRL中创建CRL 句柄,但不把它加到库中
CertDeleteCRLFromStore从证书库里删除一个CRL
CertDuplicateCRLContext通过增加引用计数来复制CRL上下文
CertEnumCRLsInStore枚举库里的CRL句柄
CertFindCertificateInCRL从指定证书里寻找CRL列表
CertFindCRLInStore在库里寻找CRL上下文
CertFreeCRLContext释放CRL上下文
CertGetCRLFromStore从库里得到CRL上下文句柄
CertSerializeCRLStoreElement串行化CRL上下文的编码CRL 和它的属性
4.2.5证书信任列表函数
CertAddCTLContextToStore把一个CTL上下文加入到证书库里
CertAddCTLLinkToStore给不同库里的CRL上下文添加链接
CertAddEncodedCTLToStore把编码CTL转化成CTL 上下文并且把它加到证书库里
CertCreateCTLContext从编码CTL中创建CTL 上下文
CertDeleteCTLFromStore从证书库里删除CTL
CertDuplicateCTLContext通过增加引用计数来复制CTL上下文
CertEnumCTLsInStore在证书库里枚举CTL上下文
CertFindCTLInStore在证书库里查找CTL上下文
CertFreeCTLContext释放CTL上下文
CertSerializeCTLStoreElement串行化CTL上下文的编码CTL 和属性
4.2.6扩展属性函数
CertEnumCertificateContextProperties枚举指定证书上下文的属性
CertEnumCRLContextProperties枚举指定CRL上下文的属性
CertEnumCTLContextProperties枚举指定CTL上下文的属性
CertGetCertificateContextProperty得到证书属性
CertGetCRLContextProperty得到CRL属性
CertGetCTLContextProperty得到CTL属性
CertSetCertificateContextProperty设置证书属性
CertSetCRLContextProperty设置CRL属性
CertSetCTLContextProperty设置CTL属性
4.2.7函数详解
4.2.7.1打开/关闭系统证书库
4.2.7.1.1 CertOpenSystemStore
HCERTSTORE WINAPI CertOpenSystemStore(
HCRYPTPROV hProv,
LPCTSTR szSubsystemProtocol,
);
参数:
hProv[in] CSP句柄。如果为NULL,就为却省CSP。如果不为NULL,它必须是由CryptAcquireContext得到的CSP句柄。
szSubsystemProtocol[in]系统证书库的名称。可以为”CA”、”MY”、”ROOT”、”SPC”。
说明:
此函数用来打开通用的系统证书库。
4.2.7.1.2 CertCloseStore
BOOL WINAPI CertCloseStore(
HCERTSTORE hCertStore,
DWORD dwFlags
);
参数:
hCertStore[in]证书库句柄。
dwFlags[in]典型地,此参数为0。却省就是关闭证书库,对于为上下文分配的内存并不释放。如果想要检查并且释放所有为证书、CRL和CTL 上下文的分配的内存,就要置下列标志。
CERT_CLOSE_STORE_CHECK_FLAG检查没有释放的证书、CRL和CTL 上下文。
CERT_CLOSE_STORE_FORCE_FLAG强制释放所有和证书库相关的上下文。
说明:
此函数释放证书库句柄。
//----------------------------------------------------------------- … HCERTSTORE hSystemStore; if(hSystemStore = CertOpenSystemStore(0,"MY")) { printf("The MY system store is open. Continue.\n"); CertCloseStore(hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG); } else { printf("The MY system store did not open.\n"); exit(1); } …
4.3证书验证函数
证书验证是通过CTL和证书列表进行的。
4.3.1使用CTL的函数
CertVerifyCTLUsage验证CTL用法
CryptMsgEncodeAndSignCTL编码和验证CTL
CryptMsgGetAndVerifySigner从一个消息中获得和验证CTL
CryptMsgSignCTL对包含CTL的消息进行签名
4.3.2证书链验证函数
CertCreateCertificateChainEngine为应用程序创建一个新的非却省的链引擎
CertCreateCTLEntryFromCertificateContextProperties创建一个CTL入口
CertDuplicateCertificateChain通过增加引用计数来复制证书链
CertFindChainInStore在证书库里查找证书链
CertFreeCertificateChain释放证书链
CertFreeCertificateChainEngine释放证书链引擎
CertGetCertificateChain从最后一个证书建立一个上下文链表
CertSetCertificateContextPropertiesFromCTLEntry通过CTL入口属性来设置证书上下文的属性
CertIsValidCRLForCertificate通过检查CRL来确定CRL 是否包括指定被撤销的证书
CertVerifyCertificateChainPolicy通过检查证书链来确定它的完整性
4.4消息函数
CryptoAPI消息函数包括两组:低级消息函数和简化消息函数。
4.4.1低级消息函数
CryptMsgCalculateEncodedLength计算加密消息的长度
CryptMsgClose关闭加密消息的句柄
CryptMsgControl执行指定的控制函数
CryptMsgCountersign标记消息中已存在的签名
CryptMsgCountersignEncoded标记已存在的签名
CryptMsgDuplicate通过增加引用计数来复制加密消息句柄
CryptMsgGetParam对加密消息进行编码或者解码后得到的参数
CryptMsgOpenToDecode打开加密消息进行解码
CryptMsgOpenToEncode打开加密消息进行编码
CryptMsgUpdate更新加密消息的内容
CryptMsgVerifyCountersignatureEncoded验证SignerInfo结构中标记时间
CryptMsgVerifyCountersignatureEncodedEx验证SignerInfo结构中标记时间,签名者可以是CERT_PUBLIC_KEY_INFO结构
4.4.2简化消息函数
CryptDecodeMessage对加密消息进行解码
CryptDecryptAndVerifyMessageSignature对指定消息进行解密并且验证签名者
CryptDecryptMessage解密指定消息
CryptEncryptMessage加密指定消息
CryptGetMessageCertificates返回包含消息的证书和CRL的证书库
CryptGetMessageSignatureCount返回签名消息的签名者数量
CryptHashMessage创建消息的哈希
CryptSignAndEncryptMessage对消息进行签名并且加密
CryptSignMessage对消息进行签名
CryptVerifyDetachedMessageHash验证包含已解邦定哈希的哈希消息
CryptVerifyDetachedMessageSignature验证包含已解邦定签名的签名消息
CryptVerifyMessageHash验证一个哈希消息
CryptVerifyMessageSignature验证一个签名消息
4.5辅助函数
4.5.1数据管理函数
CertCompareCertificate比较两个证书是否相同
CertCompareCertificateName通过比较两个证书名称来决定他们是否相同
CertCompareIntegerBlob比较两个整数BLOB
CertComparePublicKeyInfo通过比较两个证书公钥来决定他们是否相同
CertFindAttribute通过OID来查找属性
CertFindExtension通过OID来查找扩展
CertFindRDNAttr通过OID来查找RDN 属性
CertGetIntendedKeyUsage从证书中取得相关密钥用法
CertGetPublicKeyLength从公钥BLOB中取得公钥/私钥长度
CertIsRDNAttrsInCertificateName通过指定RDN数组属性比较证书名称属性来决定证书是否已包含了所有属性
CertVerifyCRLRevocation验证主题证书是否在CRL中
CertVerifyCRLTimeValidity验证CRL的有效时间
CertVerifyRevocation验证主题证书是否在CRL中
CertVerifyTimeValidity验证CRL的有效时间
CertVerifyValidityNesting验证主题时间的有效性是否在发行者有效时间内
CryptExportPublicKeyInfo导出公钥信息
CryptExportPublicKeyInfoEx导出公钥信息(用户可以指定算法)
CryptFindCertificateKeyProvInfo枚举CSP和它的密钥容器来查找对应于公钥的相应私钥
CryptFindLocalizedName查找指定名字的局部化名称
CryptHashCertificate哈希证书内容
CryptHashPublicKeyInfo计算公钥信息的哈希
CryptHashToBeSigned计算签名内容的信息哈希值
CryptImportPublicKeyInfo把公钥信息导入CSP并且返回它的句柄
CryptImportPublicKeyInfoEx把公钥信息导入CSP并且返回它的句柄
CryptMemAlloc分配内存
CryptMemFree释放内存
CryptMemRealloc重新分配内存
CryptQueryObject得到BLOB或文件的内容信息
CryptSignAndEncodeCertificate对信息进行签名并且编码
CryptSignCertificate对证书进行签名
CryptVerifyCertificateSignature使用公钥信息对主题证书或CRL的签名进行验证
CryptVerifyCertificateSignatureEx使用公钥信息对主题证书或CRL的签名进行验证
4.5.2数据转换函数
CertAlgIdToOID把CSP算法标示符转换成OID
CertGetNameString得到证书的主题或颁发者名称并且把它转换成字符串
CertNameToStr把证书名称BLOB转换成字符串
CertOIDToAlgId把OID转换成CSP 算法表示符
CertRDNValueToStr把名称值转换成字符串
CertStrToName把字符串转换成编码证书名称
CryptBinaryToString把二进制序列转换成字符串
CryptFormatObject格式化编码数据,返回Unicode字符串
CryptStringToBinary把格式化的字符串转换成二进制序列
4.5.3增强密钥用法函数
CertAddEnhancedKeyUsageIdentifier在证书EKU属性中增加一个用法标示符
CertGetEnhancedKeyUsage获得证书的EKU扩展或属性信息
CertRemoveEnhancedKeyUsageIdentifier从证书EKU扩展属性中删除用法标示符OID
CertSetEnhancedKeyUsage设置证书的EKU属性
4.5.4密钥标示函数
CryptCreateKeyIdentifierFromCSP创建CSP公钥的密钥标示符
CryptEnumKeyIdentifierProperties枚举标示符和其属性
CryptGetKeyIdentifierProperty从指定密钥标示符中获得指定属性
CryptSetKeyIdentifierProperty设置指定密钥标示符的属性
4.5.5证书库回调函数
CertDllOpenStoreProv定义库提供者打开函数
CertStoreProvCloseCallback决定当证书库引用计数为0时将发生的动作
CertStoreProvDeleteCertCallback决定当从证书库中删除一个证书之前的动作
CertStoreProvDeleteCRLCallback决定当从证书库中删除一个CRL之前的动作
CertStoreProvReadCertCallback保留
CertStoreProvReadCRLCallback保留
CertStoreProvSetCertPropertyCallback决定在CertSetCertificateContextProperty和
CertGetCertificateContext调用之前的动作
CertStoreProvSetCRLPropertyCallback决定在CertSetCRLContextProperty和
CertGetCRLContextProperty调用之前的动作
CertStoreProvWriteCertCallback决定在证书库中加入一个证书前的动作
CertStoreProvWriteCRLCallback决定在证书库中加入一个CRL前的动作
CertStoreProvReadCTL读CSP的CTL 上下文
CertStoreProvWriteCTL决定CTL是否可被加入到证书库中
CertStoreProvDeleteCTL决定CTL是否可被删除
CertStoreProvSetCTLProperty决定是否可以设置CTL的属性
CertStoreProvControl当缓冲库和存储库不同时,通知应用程序
CertStoreProvFindCert在证书库中查找下一个证书
CertStoreProvFreeFindCert释放前一个找到的证书上下文
CertStoreProvGetCertProperty得到指定的证书属性
CertStoreProvFindCRL查找第一个或下一个匹配的CRL
CertStoreProvFreeFindCRL释放前一个找到的CRL上下文
CertStoreProvGetCRLProperty得到指定CRL属性
CertStoreProvFindCTL查找第一个或下一个匹配的CTL
CertStoreProvFreeFindCTL释放前一个找到的CTL上下文
CertStoreProvGetCTLProperty得到指定CTL属性
4.5.6 OID支持函数
CryptEnumOIDFuction枚举由编码类型、函数名和OID指定注册的OID函数
CryptEnumOIDInfo枚举注册的OID信息
CryptEnumOIDInfo使用指定的密钥和组查找OID信息
CryptFreeOIDFuctionAddress释放OID函数地址句柄
CryptGetDefaultOIDDllList对于指定的函数结合和类型获得却省注册的DLL入口
CryptGetDefaultOIDFuctionAddress获得已安装的第一次或下一个却省函数或者加载包含却省函数的DLL
CryptGetOIDFuctionAddress搜索匹配指定编码类型和OID函数列表,如果没有找到,就查找注册表。
CryptGetOIDFuctionValue获得指定编码类型、函数名称和OID的值
CryptInitOIDFuctionSet初始化OID函数集合的句柄
CryptInstallOIDFuctionAddress安装可调用的OID函数地址集合
CryptRegisterDefaultOIDFuction注册包含却省函数的DLL
CryptRegisterOIDFuction注册包含指定函数的DLL
CryptRegisterOIDInfo注册由CRYPT_OID_INFO指定的OID 信息
CryptSetOIDFuctionValue设置编码类型、函数名称等的值
CryptUnregisterDefaultOIDFunction卸载包含却省函数的DLL
CryptUnregisterOIDFuction卸载包含函数的DLL
CryptUnregisterOIDInfo卸载指定OID的信息
4.5.7远程对象恢复函数
CryptGetObjectUrl从证书、CTL或CRL 中取得远程对象的URL
CryptRetrieveObjectByUrl由URL指定位置恢复PKI 对象
4.5.8 PFX函数
PFXExportCertStore从证书库中导出证书或证书和私钥
PFXExportCertStoreEx从证书库中导出证书或证书和私钥
PFXImportCertStore从PFX BLOB导入到指定证书库
PFXIsPFXBlob把外层BLOB像pfx 包那样解码
PFXVerifyPassword把外层BLOB像pfx 包那样解码,并且用指定口令解密
一些加密解密标准函数示例——DES,RSA
一. DES加密、解密
//默认密钥向量 private byte[] Keys = { 0xEF, 0xAB, 0x56, 0x78, 0x90, 0x34, 0xCD, 0x12 }; /// <summary> /// DES加密字符串 /// </summary> /// <param name="encryptString">待加密的字符串</param> /// <param name="encryptKey">加密密钥,要求为8位</param> /// <returns>加密成功返回加密后的字符串,失败返回源串</returns> public string EncryptDES(string encryptString, string encryptKey) { try { byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8)); byte[] rgbIV = Keys; byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString); DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider(); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write); cStream.Write(inputByteArray, 0, inputByteArray.Length); cStream.FlushFinalBlock(); return Convert.ToBase64String(mStream.ToArray()); } catch { return encryptString; } } /// <summary> /// DES解密字符串 /// </summary> /// <param name="decryptString">待解密的字符串</param> /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param> /// <returns>解密成功返回解密后的字符串,失败返源串</returns> public string DecryptDES(string decryptString, string decryptKey) { try { byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey.Substring(0, 8)); byte[] rgbIV = Keys; byte[] inputByteArray = Convert.FromBase64String(decryptString); DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider(); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write); cStream.Write(inputByteArray, 0, inputByteArray.Length); cStream.FlushFinalBlock(); return Encoding.UTF8.GetString(mStream.ToArray()); } catch { return decryptString; } }
二. DES/3DES加密算法源代码
/* Project xxxx * Package com.xxxx.utils * */ package com.xxxxx.utils; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.security.NoSuchAlgorithmException; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; public class DES { private String Algorithm = "DESede"; //"DESede" for Triple DES private KeyGenerator keygen; private SecretKey deskey; private Cipher c; private byte[] cipherByte; public DES() { init(); } public DES(String filename) { init(filename); } public SecretKey genKey() { try { keygen = KeyGenerator.getInstance(Algorithm); deskey = keygen.generateKey(); //利用JavaBean的持久化将key保存为文件XXX.key } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } return deskey; } /** * * @param filename */ public void init(String filename) { // restore key Security.addProvider(new com.sun.crypto.provider.SunJCE()); try { FileInputStream fis = new FileInputStream(filename); fis = new FileInputStream(filename); ObjectInputStream ois = new ObjectInputStream(fis); deskey = (SecretKey) ois.readObject(); ois.close(); fis.close(); c = Cipher.getInstance(Algorithm); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (NoSuchPaddingException ex) { ex.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } } public void init() { Security.addProvider(new com.sun.crypto.provider.SunJCE()); try { keygen = KeyGenerator.getInstance(Algorithm); deskey = keygen.generateKey(); c = Cipher.getInstance(Algorithm); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (NoSuchPaddingException ex) { ex.printStackTrace(); } } public byte[] encryptor(String str) { try { c.init(Cipher.ENCRYPT_MODE, deskey); cipherByte = c.doFinal(str.getBytes()); } catch (java.security.InvalidKeyException ex) { ex.printStackTrace(); } catch (javax.crypto.BadPaddingException ex) { ex.printStackTrace(); } catch (javax.crypto.IllegalBlockSizeException ex) { ex.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } return cipherByte; } public String decryptor(byte[] buff) { try { c.init(Cipher.DECRYPT_MODE, deskey); cipherByte = c.doFinal(buff); } catch (java.security.InvalidKeyException ex) { ex.printStackTrace(); } catch (javax.crypto.BadPaddingException ex) { ex.printStackTrace(); } catch (javax.crypto.IllegalBlockSizeException ex) { ex.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } return (new String(cipherByte)); } } /* * Project xxxx * Package com.xxxx.utils * Created on 2003-11-24 * Author Derys * */ package com.xxxx.utils; /** * * */ public class XXXDES { private static XXXDES instance = new XXXDES(); public XXXDES() { } /** * * @param str * @return */ public String encrypt(String str) { StringBuffer buffer = new StringBuffer(); DES des = new DES("XXX.key"); byte[] en = des.encryptor(str); for (int i = 0; i < en.length; i++) { buffer.append((char) en[i]); } return buffer.toString(); } /** * * @param str * @return */ public String decrypt(String str) { DES des = new DES("XXX.key"); byte[] en = new byte[str.length()]; for (int i = 0; i < str.length(); i++) { en[i] = (byte) str.charAt(i); } String de = des.decryptor(en); return de; } /** * * @param str * @return */ public String replaceChar(String str) { StringBuffer buffer = new StringBuffer(); int index = str.indexOf("'"); if (index == -1) { return str; } while (index > -1) { buffer.append(str.substring(0, index)); buffer.append("''"); str = str.substring(index + 1); index = str.indexOf("'"); } buffer.append(str); return buffer.toString(); } /** * * @return */ public static XXXDES getInstance(){ if(instance == null){ instance = new XXXDES(); } return instance; } }
使用:
encryptedTextXXXDES.getInstance().encrypt("your plain text");
plainText=XXXDES.getInstance().decrypt("your encrypted text");
三.RSA加密、解密
RSA加密算法是一种非对称加密算法。在公钥加密标准和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
RSA算法的可靠性基于分解极大的整数是很困难的。假如有人找到一种很快的分解因子的算法的话,那么用RSA加密的信息的可靠性就肯定会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA钥匙才可能被强力方式解破。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。
1. rsa基本结构
struct { int pad; long version; const rsa_method *meth; engine *engine; bignum *n; n=p*q; bignum *e; //公开的加密指数,经常为65537(ox10001) bignum *d; //私钥 bignum *p; //大素数p bignum *q; //大素数q bignum *dmp1; //d mod (p-1) bignum *dmq1; //d mod (q-1) bignum *iqmp; //(inverse of q) mod p int references; int flags; // ... }rsa;
2.初始化函数
rsa * rsa_new(void);初始化一个rsa结构
void rsa_free(rsa *rsa);释放一个rsa结构
3.rsa私钥产生函数
rsa *rsa_generate_key(int num, unsigned long e,void (*callback)(int,int,void *), void *cb_arg);产生一个模为num位的密钥对,e为公开的加密指数,一般为65537(ox10001),假如后两个参数不为null,将有些调用。在产生密钥对之前,一般需要指定随机数种子
4.判断位数函数
int rsa_size(const rsa *rsa);返回rsa模的位数,他用来判断需要给加密值分配空间的大小
int rsa_check_key(rsa *rsa);他测试p,q是否为素数,n=p*q,d*e = 1 mod (p-1*q-1), dmp1, dmq1, iqmp是否均设置正确了。
5.rsa的rsa_method函数
了解rsa的运算那就必须了解rsa_method,下面我们先看看rsa_method结构
typedef struct rsa_meth_st
{
const char *name;
int (*rsa_pub_enc)(int flen,const unsigned char *from, unsigned char *to,rsa *rsa,int padding);
int (*rsa_pub_dec)(int flen,const unsigned char *from, unsigned char *to,rsa *rsa,int padding);
int (*rsa_priv_enc)(int flen,const unsigned char *from, unsigned char *to, rsa *rsa,int padding);
int (*rsa_priv_dec)(int flen,const unsigned char *from, unsigned char *to,rsa *rsa,int padding);
int (*rsa_mod_exp)(bignum *r0,const bignum *i,rsa *rsa);
int (*bn_mod_exp)(bignum *r, const bignum *a, const bignum *p, const bignum *m, bn_ctx *ctx,bn_mont_ctx *m_ctx);
int (*init)(rsa *rsa); /* called at new */
int (*finish)(rsa *rsa); /* called at free */
int flags; /* rsa_method_flag_* things */ char *app_data; /* may be needed! */
int (*rsa_sign)(int type,const unsigned char *m, unsigned int m_length,unsigned char *sigret, unsigned int *siglen, const rsa *rsa);
int (*rsa_verify)(int dtype,const unsigned char *m, unsigned int m_length,unsigned char *sigbuf, unsigned int siglen, const rsa *rsa);
} rsa_method;
const rsa_method *rsa_pkcs1_ssleay(void);
const rsa_method *rsa_null_method(void);
主要有上面两个函数。第二个函数是定义了rsa_null才会调用,其实要调用这个函数以后几乎什么都不能干,只是输出错误信息。第一个是常用的method,下面我们看看它的定义
const rsa_method *rsa_pkcs1_ssleay(void)
{
return(&rsa_pkcs1_eay_meth);
}
static rsa_method rsa_pkcs1_eay_meth={ "eric young's pkcs#1 rsa", rsa_eay_public_encrypt, rsa_eay_public_decrypt, /* signature verification */ rsa_eay_private_encrypt, /* signing */ rsa_eay_private_decrypt, rsa_eay_mod_exp, bn_mod_exp_mont, rsa_eay_init, rsa_eay_finish, 0, /* flags */ null, 0, /* rsa_sign */ 0 /* rsa_verify */ };
由此可以看出,一般rsa->meth-> rsa_pub_enc对应于rsa_eay_public_encrypt,刚开始看openssl的时候最难得就是这个指向函数的指针,根本不知道rsa->meth-> rsa_pub_enc对应于哪里。在openssl里面这种指针很多,到以后也能够看到。下面是设置meth的一些函数应该都很容易理解
void rsa_set_default_method(const rsa_method *meth);
const rsa_method *rsa_get_default_method(void);
int rsa_set_method(rsa *rsa, const rsa_method *meth);
const rsa_method *rsa_get_method(const rsa *rsa);
int rsa_flags(const rsa *rsa);
rsa *rsa_new_method(engine *engine);
6.加解密函数
int rsa_public_encrypt(int flen, unsigned char *from, unsigned char *to, rsa *rsa, int padding);
int rsa_private_decrypt(int flen, unsigned char *from, unsigned char *to, rsa *rsa, int padding);
int rsa_private_encrypt(int flen, unsigned char *from, unsigned char *to, rsa *rsa,int padding);
int rsa_public_decrypt(int flen, unsigned char *from, unsigned char *to, rsa *rsa,int padding);
假如rsa_set_method(rsa, rsa_pkcs1_ssleay())的话,那rsa_public_encrypt对应于rsa_eay_public_encrypt,这样我们就可以调试公钥加密的过程了。flen为要加密信息的长度,from为需要加密的信息,to为加密后的信息,一般to至少要申请bn_num_bytes(rsa->n)大的空间。padding是采取的加解密方案。pkcs#1中主要提供了两种加密方案,rsaex-oaep和psaes-pkcs1-v1_5(反正就是两种加密过程了,有点复杂,它主要是先对先对需要加密的数据进行了编码,比如rsaes-oaep采用eme-oaep编码,再进行加密或解密)。编码的函数:
case rsa_pkcs1_padding: i=rsa_padding_add_pkcs1_type_2(buf,num,from,flen);
#ifndef openssl_no_sha case rsa_pkcs1_oaep_padding: i=rsa_padding_add_pkcs1_oaep(buf,num,from,flen,null,0);
#endif case rsa_sslv23_padding: i=rsa_padding_add_sslv23(buf,num,from,flen);
case rsa_no_padding: i=rsa_padding_add_none(buf,num,from,flen);等上面编好码后,就调用bn_mod_exp_mont来进行模幂了。最后得出值,这也就是具体的加密和解密过程。在这里还可以发现,加密时输入的rsa有两种方式,一是p,q,...为null,只有rsa->d,和rsa->n不为空,这样就直接用rsa->d和rsa->n进行模幂计算,假如p,q.....都不为空的话,他会调用中国剩余定理来进行加密。
7.签名函数
int rsa_sign(int type, unsigned char *m, unsigned int m_len, unsigned char *sigret, unsigned int *siglen, rsa *rsa);
int rsa_verify(int type, unsigned char *m, unsigned int m_len, unsigned char *sigbuf, unsigned int siglen, rsa *rsa);其实签名其实和用私钥加密差不多是一回事,所以签名函数最终调用的就是私钥加密的函数,在openssl中这个签名函数很少单独拿出来用的,都是为了给evp_signfinal来调用的。所以假如是利用rsa进行签名的话,rsa_private_encrypt,bn_mod_exp_mont是最基本的,所有的都需要调用他,区别无非就在于在需要签名的信息上做了一下处理(一般将需要签名的信息求取摘要值得到m)
8.写入文件函数
int rsa_print(bio *bp, rsa *x, int offset);
int rsa_print_fp(file *fp, rsa *x, int offset);offset是为了调整输出格式的,随意一个数都可以(例如2,12,16。。)
9.其他
int rsa_blinding_on(rsa *rsa, bn_ctx *ctx);
void rsa_blinding_off(rsa *rsa);为了防止时间攻击,openssl还在签名的时候产生一个随机因子,附加在私钥上。
int rsa_sign_asn1_octet_string(int dummy, unsigned char *m,unsigned int m_len, unsigned char *sigret, unsigned int *siglen,rsa *rsa);
int rsa_verify_asn1_octet_string(int dummy, unsigned char *m,unsigned int m_len, unsigned char *sigbuf, unsigned int siglen,rsa *rsa);
代码示例如下:
using System; using System.Security.Cryptography; using System.IO; using System.Text; namespace Microsoft.Samples.Security.PublicKey { class App { // Main entry point static void Main(string[] args) { // Instantiate 3 People for example. See the Person class below Person alice = new Person("Alice"); Person bob = new Person("Bob"); Person steve = new Person("Steve"); // Messages that will exchanged. See CipherMessage class below CipherMessage aliceMessage; CipherMessage bobMessage; CipherMessage steveMessage; // Example of encrypting/decrypting your own message Console.WriteLine("Encrypting/Decrypting Your Own Message"); Console.WriteLine("-----------------------------------------"); // Alice encrypts a message using her own public key aliceMessage = alice.EncryptMessage("Alice wrote this message"); // then using her private key can decrypt the message alice.DecryptMessage(aliceMessage); // Example of Exchanging Keys and Messages Console.WriteLine(); Console.WriteLine("Exchanging Keys and Messages"); Console.WriteLine("-----------------------------------------"); // Alice Sends a copy of her public key to Bob and Steve bob.GetPublicKey(alice); steve.GetPublicKey(alice); // Bob and Steve both encrypt messages to send to Alice bobMessage = bob.EncryptMessage("Hi Alice! - Bob."); steveMessage = steve.EncryptMessage("How are you? - Steve"); // Alice can decrypt and read both messages alice.DecryptMessage(bobMessage); alice.DecryptMessage(steveMessage); Console.WriteLine(); Console.WriteLine("Private Key required to read the messages"); Console.WriteLine("-----------------------------------------"); // Steve cannot read the message that Bob encrypted steve.DecryptMessage(bobMessage); // Not even Bob can use the Message he encrypted for Alice. // The RSA private key is required to decrypt the RS2 key used // in the decryption. bob.DecryptMessage(bobMessage); } // method Main } // class App class CipherMessage { public byte[] cipherBytes; // RC2 encrypted message text public byte[] rc2Key; // RSA encrypted rc2 key public byte[] rc2IV; // RC2 initialization vector } class Person { private RSACryptoServiceProvider rsa; private RC2CryptoServiceProvider rc2; private string name; // Maximum key size for the RC2 algorithm const int keySize = 128; // Person constructor public Person(string p_Name) { rsa = new RSACryptoServiceProvider(); rc2 = new RC2CryptoServiceProvider(); rc2.KeySize = keySize; name = p_Name; } // Used to send the rsa public key parameters public RSAParameters SendPublicKey() { RSAParameters result = new RSAParameters(); try { result = rsa.ExportParameters(false); } catch (CryptographicException e) { Console.WriteLine(e.Message); } return result; } // Used to import the rsa public key parameters public void GetPublicKey(Person receiver) { try { rsa.ImportParameters(receiver.SendPublicKey()); } catch (CryptographicException e) { Console.WriteLine(e.Message); } } public CipherMessage EncryptMessage(string text) { // Convert string to a byte array CipherMessage message = new CipherMessage(); byte[] plainBytes = Encoding.Unicode.GetBytes(text.ToCharArray()); // A new key and iv are generated for every message rc2.GenerateKey(); rc2.GenerateIV(); // The rc2 initialization doesnt need to be encrypted, but will // be used in conjunction with the key to decrypt the message. message.rc2IV = rc2.IV; try { // Encrypt the RC2 key using RSA encryption message.rc2Key = rsa.Encrypt(rc2.Key, false); } catch (CryptographicException e) { // The High Encryption Pack is required to run this sample // because we are using a 128-bit key. See the readme for // additional information. Console.WriteLine("Encryption Failed. Ensure that the" + " High Encryption Pack is installed."); Console.WriteLine("Error Message: " + e.Message); Environment.Exit(0); } // Encrypt the Text Message using RC2 (Symmetric algorithm) ICryptoTransform sse = rc2.CreateEncryptor(); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, sse, CryptoStreamMode.Write); try { cs.Write(plainBytes, 0, plainBytes.Length); cs.FlushFinalBlock(); message.cipherBytes = ms.ToArray(); } catch (Exception e) { Console.WriteLine(e.Message); } finally { ms.Close(); cs.Close(); } return message; } // method EncryptMessage public void DecryptMessage(CipherMessage message) { // Get the RC2 Key and Initialization Vector rc2.IV = message.rc2IV; try { // Try decrypting the rc2 key rc2.Key = rsa.Decrypt(message.rc2Key, false); } catch (CryptographicException e) { Console.WriteLine("Decryption Failed: " + e.Message); return; } ICryptoTransform ssd = rc2.CreateDecryptor(); // Put the encrypted message in a memorystream MemoryStream ms = new MemoryStream(message.cipherBytes); // the CryptoStream will read cipher text from the MemoryStream CryptoStream cs = new CryptoStream(ms, ssd, CryptoStreamMode.Read); byte[] initialText = new Byte[message.cipherBytes.Length]; try { // Decrypt the message and store in byte array cs.Read(initialText, 0, initialText.Length); } catch (Exception e) { Console.WriteLine(e.Message); } finally { ms.Close(); cs.Close(); } // Display the message received Console.WriteLine(name + " received the following message:"); Console.WriteLine(" " + Encoding.Unicode.GetString(initialText)); } // method DecryptMessage } // class Person } // namespace PublicKey