David.Turing's blog

           

          CryptoAPI第一天

          [轉]http://www.chinaitpower.com/A200507/2005-07-27/175804.html

          一:準備工作
          一般必須包含如下頭
          文件 以及定義
          #include
          #include
          #include
          #define MY_ENCODING_TYPE? (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
          ?當包含wincrypt.h頭
          文件 時,一般都需要定義#define _WIN32_WINNT 0x(具體的值),否則將得到如下錯誤:error C2065: undeclared identifier,不同的操作 系統 不同定義如下:
          Windows Server 2003 family _WIN32_WINNT>=0x0502
          WINVER>=0x0502
          Windows XP _WIN32_WINNT>=0x0501
          WINVER>=0x0501
          Windows 2000 _WIN32_WINNT>=0x0500
          WINVER>=0x0500
          Windows NT 4.0 _WIN32_WINNT>=0x0400
          WINVER>=0x0400
          Windows Me _WIN32_WINDOWS=0x0500
          WINVER>=0x0500
          Windows 98 _WIN32_WINDOWS>=0x0410
          WINVER>=0x0410
          Windows 95 _WIN32_WINDOWS>=0x0400
          WINVER>=0x0400
          Internet Explorer 6.0 _WIN32_IE>=0x0600
          Internet Explorer 5.6 _WIN32_IE>=0x0560
          Internet Explorer 5.01, 5.5 _WIN32_IE>=0x0501
          Internet Explorer 5.0, 5.0a, 5.0b _WIN32_IE>=0x0500
          Internet Explorer 4.01 _WIN32_IE>=0x0401
          Internet Explorer 4.0 _WIN32_IE>=0x0400
          Internet Explorer 3.0, 3.01, 3.02 _WIN32_IE>=0x0300

          二:了解基本知識
          CryptoAPI的配置信息存儲在注冊表中,包括如下密鑰:
          HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \ Cryptography \Defaults
          HKEY_CURRENT_USER\ Software \ Microsoft\ Cryptography \Providers
          ---- CryptoAPI使用兩種密鑰:會話密鑰與公共/私人密鑰對。會話密鑰使用相同的加密和解密密鑰,這種算法較快,但必須保證密鑰的安全傳遞。公共/私人密鑰對使用一個公共密鑰和一個私人密鑰,私人密鑰只有專人才能使用,公共密鑰可以廣泛傳播。如果密鑰對中的一個用于加密,另一個一定用于解密。公共/私人密鑰對算法很慢,一般只用于加密小批數據,例如用于加密會話密鑰。CryptoAPI支持兩種基本的編碼方法:流式編碼和塊編碼。流式編碼在明碼文本的每一位上創建編碼位,速度較快,但安全性較低。塊編碼在一個完整的塊上(一般為64位)上工作,需要使用填充的方法對要編碼的數據進行舍入,以組成多個完整的塊。這種算法速度較慢,但更安全。

          三:下面進入具體的編程?
          一: Creating a Key Container and Generating Keys
          ? 創建一個密鑰容器,在進行加密,解密
          文件 ,并且簽名的時候,必須需要一個公/私鑰對,下面我們就來創建默認的密鑰容器,要注意的是創建密鑰容器并不會自動產生公/私鑰對.
          ? 下面是我們
          程序 的任務:
          ? 1,假如密鑰容器不存在則創建一個。
          ? 2,假如簽名密鑰不存在則在密鑰容器里創建一個。
          ? 3,假如交換密鑰不存在則在密鑰容器里創建一個。
          ? 4,獲取CSP中的一些參數
          ? 下面是具體的步驟:
          ? 1,連接缺省的CSP
          BOOL WINAPI CryptAcquireContext(
          ? HCRYPTPROV* phProv,?? //out
          ? LPCTSTR pszContainer, //in
          ? LPCTSTR pszProvider,? //in
          ? DWORD dwProvType,???? //in
          ? DWORD dwFlags???????? //in
          );
          第一個參數是返回的CSP句柄,第二個是密鑰容器的名字,第三個是A null-terminated string that specifies the name of the CSP to be used.第四個是指定提供的類型。例如:CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0));
          如果當前機器的未曾設置過缺省的密鑰容器,因此必須為機器創建缺省的密鑰容器。
          CryptAcquireContext( &hCryptProv, UserName, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)大家有沒有看到,只是最后一個參數不同而已,多了一個CRYPT_NEWKEYSET而已。
          ? 2,取得CSP的參數
          BOOL WINAPI CryptGetProvParam(
          ? HCRYPTPROV hProv,
          ? DWORD dwParam,
          ? BYTE* pbData,
          ? DWORD* pdwDataLen,
          ? DWORD dwFlags
          );
          第一個參數是CSP的句柄,第二個參數是需要取得的具體參數對象(類型比較多,具體請看MSDN)。
          例子:CryptGetProvParam(hCryptProv, PP_CONTAINER, (BYTE *)szUserName, &dwUserNameLen, 0)
          ? 3,函數返回所獲取密鑰類型的句柄(0表失敗,非0表成功)
          BOOL WINAPI CryptGetUserKey(
          ? HCRYPTPROV hProv,
          ? DWORD dwKeySpec,
          ? HCRYPTKEY* phUserKey
          );
          ? 參數比較簡單,只談談第二次參數,它可以是AT_KEYEXCHANGE(交換密鑰) or AT_SIGNATURE(簽名密鑰),例如:
          CryptGetUserKey(hCryptProv,AT_KEYEXCHANGE,&hKey)
          ? 4,產生一個隨機的交換密鑰或者公/私鑰對
          BOOL WINAPI CryptGenKey(
          ? HCRYPTPROV hProv,
          ? ALG_ID Algid,
          ? DWORD dwFlags,
          ? HCRYPTKEY* phKey
          );
          ALG_ID 表明產生私鑰所使用的算法。有如下參數:
          微軟 提供的基本算法
          CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1,CALG_MAC,CALG_HMAC,CALG_SSL3_SHAMD5,CALG_MD2,CALG_MD2
          CALG_RSA_SIGN,CALG_RSA_KEYX,CALG_RC2,CALG_RC4,CALG_DES
          微軟 提供的增強型算法:
          CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1
          CALG_MAC,CALG_HMAC ,CALG_SSL3_SHAMD5,CALG_RSA_SIGN,CALG_RSA_KEYX,CALG_RC2,CALG_RC4,CALG_DES,CALG_3DES_112,CALG_3DES
          使用DH的CSP有如下兩個參數,CALG_DH_EPHEM,CALG_DH_SF
          使用公開密鑰算法:AT_KEYEXCHANGE,AT_SIGNATURE
          dwFlags,,表示密鑰使用的長度,參數可以為0,采用默認的密鑰長度。或者是進行如下幾個參數的或:
          CRYPT_ARCHIVABLE:表示在句柄在關閉之前都能夠被導出
          CRYPT_CREATE_SALT:表示密鑰按照一個salt value來隨機產生。
          CRYPT_EXPORTABLE:表示密鑰可以從CSP中導出到BLOB,因為會話密鑰產生是必須可導出的,所以必須設置
          CRYPT_NO_SALT:表示沒有SALT VALUE獲取allocated for a forty-bit symmetric key
          CRYPT_PREGEN:表示在DH或者DSS密鑰產生必須有個初始化。
          例如:CryptGenKey(hCryptProv,AT_KEYEXCHANGE,0,&hKey)
          ? 5,釋放CSP句柄
          BOOL WINAPI CryptReleaseContext(
          ? HCRYPTPROV hProv,
          ? DWORD dwFlags //保留字,現在必須為0
          );
          ? 6,為CSP增加一個reference count(用來跟蹤COM對象的整數值,當對象創建,值為1。每次對對象的操作都將增加,而對對象的關閉將減少,當值為0是,對象釋放,所以與對象相關操作將無效)
          BOOL WINAPI CryptContextAddRef(
          ? HCRYPTPROV hProv,
          ? DWORD* pdwReserved,? //保留字,必須為NULL
          ? DWORD dwFlags??????? //保留字,必須為0
          );

          ? 二:Deriving a Session Key from a Password
          ? 1,連接CSP
          ? 2,使用CryptCreateHash產生一個空的HASH對象
          ? 3,對密碼進行HASH處理
          ? 4,釋放HASH以及密碼對象
          ? 5,釋放CSP
          ? 下面是具體的步驟:
          ? 1,CryptCreateHash初始化一個HASH對象
          BOOL WINAPI CryptCreateHash(
          ? HCRYPTPROV hProv,? //in
          ? ALG_ID Algid,????? //in
          ? HCRYPTKEY hKey,??? //in
          ? DWORD dwFlags,???? //in保留字,必須為0
          ? HCRYPTHASH* phHash //out
          );
          第二個參數是指定HASH算法,有CALG_HMAC,CALG_MAC,CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1,CALG_SSL3_SHAMD5。第三個參數對于那些keyed hash,例如HMAC,MAC算法。但是nonkeyed算法,必須設置為0。
          ? 2,CryptHashData對數據使用HASH
          BOOL WINAPI CryptHashData(
          ? HCRYPTHASH hHash,? //in,HASH對象句柄
          ? BYTE* pbData,????? //in,待HASH的數據
          ? DWORD dwDataLen,?? //in,待HASH數據的長度,當dwFlags為CRYPT_USERDATA為0時,必須為0
          ? DWORD dwFlags????? //in,一般為0,或者為CRYPT_USERDATA(用在用戶進入
          系統 時需要輸入PIN)
          );
          ? 3,CryptDeriveKey從某一數據產生會話密鑰,他有點類似CryptGenKey,但是他產生的會話密鑰來自固定數據,而CryptGenKey是隨機產生的。并且不能產生公/私鑰對
          BOOL WINAPI CryptDeriveKey(
          ? HCRYPTPROV hProv,????? //in,CSP句柄
          ? ALG_ID Algid,????????? //in,指定的算法,類似CryptGenKey
          ? HCRYPTHASH hBaseData,? //in,HASH對象的句柄
          ? DWORD dwFlags,???????? //in,指定產生密鑰的類型
          ? HCRYPTKEY* phKey?????? //in,out產生的密鑰句柄地址
          );
          例如:CryptDeriveKey(hCryptProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hKey)
          ? 4,CryptDestroyHash(hHash);
          ? 5, CryptDestroyKey(hKey);
          ? 6, 在這里發現一個不錯的函數,就是那種提示輸入密碼的命令行(屏幕只會出現***)
          ? void GetConsoleInput(char* strInput, int intMaxChars)
          {
          ?char ch;
          ?char minChar = ' ';
          ?minChar++;
          ?ch = getch();
          ?while (ch != '\r')
          ?{
          ? if (ch == '\b' && strlen(strInput) > 0)
          ? {
          ?? strInput[strlen(strInput)-1]?? = '\0';
          ?? printf("\b \b");
          ? }
          ? else if (ch >= minChar && strlen(strInput) < intMaxChars)
          ? {
          ?? strInput[strlen(strInput)+1] = '\0';
          ?? strInput[strlen(strInput)]?? = ch;
          ?? putch('*');
          ? }
          ? ch = getch();
          ?}
          ?putch('\n');
          }

          ? 三:Duplicating,setting and getting Session key
          ? 1,連接CSP
          ? 2,使用CryptGenKey產生一個會話密鑰
          ? 3,CryptDuplicateKey復制會話密鑰
          ? 4,CryptSetKeyParam改變密鑰產生的過程
          ? 5,CryptGenRandom產生隨機數
          具體過程。
          1,CryptDuplicateKe復制會話密鑰
          BOOL WINAPI CryptDuplicateKey(
          ? HCRYPTKEY hKey,????? //in 會話密鑰句柄
          ? DWORD* pdwReserved,? //in 保留字,必須為NULL
          ? DWORD dwFlags,?????? //in 保留字,必須為0
          ? HCRYPTKEY* phKey???? //out 新的會話密鑰
          );
          2,CryptSetKeyParam定制會話密鑰的參數
          BOOL WINAPI CryptSetKeyParam(
          ? HCRYPTKEY hKey,
          ? DWORD dwParam,? //in 很多,具體請看MSDN
          ? BYTE* pbData,
          ? DWORD dwFlags? //in? 只有在dwParam=KP_ALGID才被使用
          );
          例如CryptSetKeyParam(hOriginalKey, KP_MODE, (BYTE*)&dwMode, 0)(dwMode = CRYPT_MODE_ECB)
          3,CryptGenRandom為空間產生隨機字節
          BOOL WINAPI CryptGenRandom(
          ? HCRYPTPROV hProv,
          ? DWORD dwLen,??? //需要產生的隨機比特數
          ? BYTE* pbBuffer? //需要返回數據的空間,這個pbBuffer可以等于CryptSetKeyParam的pbData
          );
          例如:CryptGenRandom(hCryptProv, 8, pbData)
          4,CryptGetKeyParam獲取密鑰的一些參數
          BOOL WINAPI CryptGetKeyParam(
          ? HCRYPTKEY hKey,
          ? DWORD dwParam,???? //in,參數眾多
          ? BYTE* pbData,????? //out,獲取BYTE數據的指針
          ? DWORD* pdwDataLen, //out,獲取BYTE數據的長度
          ? DWORD dwFlags????? //關鍵字,必須為0
          );
          例如:CryptGetKeyParam(hKey, KP_IV, pbData, &dwCount, 0)


          ? 四:Exporting a Session Key
          ? 1,連接CSP
          ? 2,CryptGetUserKey獲取公/私鑰對和交換密鑰,公私鑰用來簽名,而交換密鑰用來導出會話密鑰
          ? 3,CryptGenKey產生會話密鑰
          ? 4,CryptExportKey創建簡單包含有會話密鑰的key BLOB
          ? 5,釋放處理:
          ? 具體過程:
          1,CryptExportKey函數導出密鑰
          BOOL WINAPI CryptExportKey(
          ? HCRYPTKEY hKey,???? //需要導出的密鑰句柄
          ? HCRYPTKEY hExpKey,? //將待導出密鑰用交換密鑰進行加密,假如是公開的BLOG當然就設置為0
          ? DWORD dwBlobType,?? //指定導出的密鑰BLOB類型。六個參數見MSDN
          ? DWORD dwFlags,????? //CRYPT_DESTROYKEY,CRYPT_SSL2_FALLBACK,CRYPT_OAEP
          ? BYTE* pbData,?????? //導出的數據指針,以后就可以將這個數據寫如磁盤或者別的任務。
          ? DWORD* pdwDataLen?? //導出的數據長度
          );
          例如:CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwBlobLen)
          2,CryptImportKey將密鑰從BLOB轉換到CSP中
          BOOL WINAPI CryptImportKey(
          ? HCRYPTPROV hProv,? //CSP句柄
          ? BYTE* pbData,????? //待轉換的BLOB數據
          ? DWORD dwDataLen,?? //待轉換的數據長度
          ? HCRYPTKEY hPubKey, //對BLOB解密的公鑰,譬如上面是用交換密鑰密鑰加密的,就用交換密鑰解密
          ? DWORD dwFlags,???? //目前還只應用在當一對公/私鑰從PRIVATEKEYBLOB中加入CSP中這種情況。
          ? HCRYPTKEY* phKey?? //out導入的密鑰
          );
          例如:CryptImportKey(hProv,pbKeyBlob,dwBlobLen,0,0,&hPubKey)


          ? 五:Encoding and Decoding Messages
          ? 編碼的處理過程
          ? 1,將待編碼的數據轉化為合適的格式,使用
          ? 2,調用CryptMsgOpenToEncode,passing the necessary argument;
          ? 3, 調用CryptMsgUpdate函數多次,最后一次調用時,將final參數設置為true
          ? 4, 調用CryptMsgGetParam來獲取一個需要得到的參數。
          ? 5, 調用CryptMsgClose來關閉消息
          ? 解碼的處理過程
          ? 1,檢查申請的放編碼后數據的空間,利用函數CryptMsgCalculateEncodedLength.
          ? 2,調用函數CryptMsgOpenToDecode,passing the necessary argument;
          ? 3,調用CryptMsgUpdate一次,這將導致合適的動作去處理信息,以來于信息的格式
          ? 4,一些額外的處理,例如額外的解密或者是驗證,調用CryptMsgControl,
          ? 5,調用CryptMsgGetParam來獲取需要得到的參數
          ? 6,調用CryptMsgClose來關閉消息
          ? 具體的函數介紹:
          ? 1,CryptMsgCalculateEncodedLength計算所需要的存儲編碼的最大空間值
          DWORD WINAPI CryptMsgCalculateEncodedLength(
          ? DWORD dwMsgEncodingType,//指定編碼類型。一般為X509_ASN_ENCODING|PKCS_7_ASN_ENCODING
          ? DWORD dwFlags,
          ? DWORD dwMsgType,
          ? const void* pvMsgEncodeInfo, //in 指向待編碼的數據,數據類型依賴于dwMsgType
          ? LPSTR pszInnerContentObjID,
          ? DWORD cbData???????????????? //in 比特數的容量
          );
          第二個參數:CMSG_BARE_CONTENT_FLAG,CMSG_DETACHED_FLAG,CMSG_CONTENTS_OCTETS_FLAG,CMSG_CMS_ENCAPSULATED_CONTENT_FLAG
          第三個參數:CMSG_DATA,CMSG_SIGNED,CMSG_ENVELOPED,CMSG_SIGNED_AND_ENVELOPED,CMSG_HASHED,CMSG_ENCRYPTED
          第五個參數:szOID_RSA_data,szOID_RSA_signedData,szOID_RSA_envelopedData,szOID_RSA_signEnvData,szOID_RSA,digestedData ,
          szOID_RSA_encryptedData,SPC_INDIRECT_DATA_OBJID,NULL
          返回值:返回需要的一個加密信息所需要的長度
          ? 2,CryptMsgOpenToEncode打開一個消息以便進行編碼,返回打開消息的句柄
          ? HCRYPTMSG WINAPI CryptMsgOpenToEncode(
          ? DWORD dwMsgEncodingType,????? //指定編碼類型。一般為X509_ASN_ENCODING|PKCS_7_ASN_ENCODING
          ? DWORD dwFlags,???????????????
          ? DWORD dwMsgType,?????????????
          ? const void* pvMsgEncodeInfo,
          ? LPSTR pszInnerContentObjID,??? //和CryptMsgCalculateEncodedLength一樣
          ? PCMSG_STREAM_INFO pStreamInfo //當流沒被使用時,該參數為NULL
          );
          第二個參數:CMSG_BARE_CONTENT_FLAG,CMSG_DETACHED_FLAG,CMSG_CONTENTS_OCTETS_FLAG,CMSG_CMS_ENCAPSULATED_CONTENT_FLAG,
          CMSG_CRYPT_RELEASE_CONTEXT_FLAG
          第三個參數:
          CMSG_DATA(Not used),CMSG_SIGNED,CMSG_SIGNED_ENCODE_INFO,CMSG_ENVELOPED,CMSG_ENVELOPED_ENCODE_INFO
          CMSG_SIGNED_AND_ENVELOPED(Not currently implemented),CMSG_HASHED
          ? 3,CryptMsgOpenToDecode打開一個消息以便進行解碼,返回打開消息的句柄
          ? CRYPTMSG WINAPI CryptMsgOpenToDecode(
          ? DWORD dwMsgEncodingType,???? //指定編碼類型。一般為X509_ASN_ENCODING|PKCS_7_ASN_ENCODING
          ? DWORD dwFlags,?? //CMSG_DETACHED_FLAG,CMSG_CRYPT_RELEASE_CONTEXT_FLAG
          ? DWORD dwMsgType, //CMSG_DATA,CMSG_ENVELOPED,CMSG_HASHED,CMSG_SIGNED,CMSG_SIGNED_AND_ENVELOPED
          ? HCRYPTPROV hCryptProv,??? //指定使用HASHING的句柄,一般設置為0
          ? PCERT_INFO pRecipientInfo,//保留字,必須為NULL
          ? PCMSG_STREAM_INFO pStreamInfo//假如流沒被使用,必須為NULL
          );
          ? 4,CryptMsgUpdate增加內容到加密信息中
          BOOL WINAPI CryptMsgUpdate(
          ? HCRYPTMSG hCryptMsg, //待更新的加密信息句柄
          ? const BYTE* pbData,? //待編碼/解碼的數據
          ? DWORD cbData,??????? // pbData 的數據長度
          ? BOOL fFinal?????????
          );
          第四個參數:當CMSG_DETACHED_FLAG沒有設置,并且信息由CryptMsgOpenToDecode或CryptMsgOpenToEncode打開,那么fFinal被設置為TRUE,并且CryptMsgUpdate只被調用一次。當CMSG_DETACHED_FLAG被設置,并且信息由 CryptMsgOpenToEncode打開,那么僅在最后一次調用CryptMsgUpdate才被設置為TRUE。當CMSG_DETACHED_FLAG被設置,并且信息由CryptMsgOpenToDecode打開,那么僅在信息頭單獨被處理時CryptMsgUpdate才被設置為TRUE。
          ? 5,CryptMsgGetParam在數據編碼/解碼后獲取參數
          ? BOOL WINAPI CryptMsgGetParam(
          ? HCRYPTMSG hCryptMsg,? //in 信息句柄
          ? DWORD dwParamType,?? //in 參數眾多,參見MSDN
          ? DWORD dwIndex,?????? //in 可適用的返回參數句柄,假如參數沒有被獲取,則被忽略或則為0
          ? void* pvData,??????? //out 獲取的數據指針
          ? DWORD* pcbData????? //in,out數據長度
          );
          ?? 6,CryptMsgClose關閉信息句柄
          BOOL WINAPI CryptMsgClose(
          ? HCRYPTMSG hCryptMsg
          );
          具體的例子:
          cbEncodedBlob = CryptMsgCalculateEncodedLength(
          ???????????? MY_ENCODING_TYPE,?????? // Message encoding type
          ???????????? 0,????????????????????? // Flags
          ???????????? CMSG_DATA,????????????? // Message type
          ???????????? NULL,?????????????????? // Pointer to structure
          ???????????? NULL,?????????????????? // Inner content object ID
          ???????????? cbContent))???????????? // Size of content
          hMsg = CryptMsgOpenToEncode(
          ????????? MY_ENCODING_TYPE,??????? // Encoding type
          ????????? 0,?????????????????????? // Flags
          ????????? CMSG_DATA,?????????????? // Message type
          ????????? NULL,??????????????????? // Pointer to structure
          ????????? NULL,??????????????????? // Inner content object ID
          ????????? NULL))?????????????????? // Stream information (not used)
          CryptMsgUpdate(
          ??????? hMsg,???????? // Handle to the message
          ??????? pbContent,??? // Pointer to the content
          ??????? cbContent,??? // Size of the content
          ??????? TRUE))??????? // Last call
          {
          CryptMsgGetParam(
          ?????????????? hMsg,????????????????????? // Handle to the message
          ?????????????? CMSG_BARE_CONTENT_PARAM,?? // Parameter type
          ?????????????? 0,???????????????????????? // Index
          ?????????????? pbEncodedBlob,???????????? // Pointer to the BLOB
          ?????????????? &cbEncodedBlob))?????????? // Size of the BLOB
          CryptMsgClose(hMsg);
          hMsg = CryptMsgOpenToDecode(
          ?????????????? MY_ENCODING_TYPE,????? // Encoding type.
          ?????????????? 0,???????????????????? // Flags.
          ?????????????? CMSG_DATA,???????????? // Look for a data message.
          ?????????????? NULL,????????????????? // Cryptographic provider.
          ?????????????? NULL,????????????????? // Recipient information.
          ?????????????? NULL))???????????????? // Stream information.

          posted on 2006-07-05 23:18 david.turing 閱讀(3458) 評論(1)  編輯  收藏 所屬分類: CryptoAPI

          評論

          # re: CryptoAPI第一天 2008-03-26 16:39 haityang

          請教您一個問題,假如我現在有一個pkcs#7 envelope,假設它是由三個證書加密的,但我沒有任何一個證書的私鑰,現在我想從這個信封中刪除一個證書.應該怎么實現?  回復  更多評論   


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           

          導航

          統計

          常用鏈接

          留言簿(110)

          我參與的團隊

          隨筆分類(126)

          隨筆檔案(155)

          文章分類(9)

          文章檔案(19)

          相冊

          搜索

          積分與排名

          最新隨筆

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 上虞市| 闽清县| 德保县| 海原县| 平谷区| 平邑县| 常德市| 航空| 苗栗市| 定南县| 南召县| 平利县| 汶川县| 油尖旺区| 和龙市| 泗水县| 德钦县| 绥阳县| 镇远县| 邓州市| 临安市| 阜阳市| 屯门区| 革吉县| 祁阳县| 岱山县| 娄烦县| 资阳市| 佳木斯市| 惠水县| 河南省| 喀喇| 霍山县| 竹山县| 眉山市| 白玉县| 鄂温| 兴隆县| 乐平市| 南安市| 竹山县|