国产一级一区二区_segui88久久综合9999_97久久夜色精品国产_欧美色网一区二区

掃一掃
關(guān)注微信公眾號(hào)

SSH通信協(xié)議淺析
2005-11-25   

第一部分:協(xié)議概覽
整個(gè)通訊過程中,經(jīng)過下面幾個(gè)階段協(xié)商實(shí)現(xiàn)認(rèn)證連接。
第一階段:
由客戶端向服務(wù)器發(fā)出 TCP 連接請(qǐng)求。TCP 連接建立后,客戶端進(jìn)入等待,服務(wù)器向客戶端發(fā)送第一個(gè)報(bào)文,宣告自己的版本號(hào),包括協(xié)議版本號(hào)和軟件版本號(hào)。協(xié)議版本號(hào)由主版本號(hào)和次版本號(hào)兩部分組成。它和軟件版本號(hào)一起構(gòu)成形如:
"SSH-<主協(xié)議版本號(hào)>.<次協(xié)議版本號(hào)>-<軟件版本號(hào)>\n"
的字符串。其中軟件版本號(hào)字符串的最大長(zhǎng)度為40個(gè)字節(jié),僅供調(diào)試使用??蛻舳私拥綀?bào)文后,回送一個(gè)報(bào)文,內(nèi)容也是版本號(hào)??蛻舳隧憫?yīng)報(bào)文里的協(xié)議版本號(hào)這樣來決定:當(dāng)與客戶端相比服務(wù)器的版本號(hào)較低時(shí),如果客戶端有特定的代碼來模擬,則它發(fā)送較低的版本號(hào);如果它不能,則發(fā)送自己的版本號(hào)。當(dāng)與客戶端相比服務(wù)器的版本號(hào)較高時(shí),客戶端發(fā)送自己的較低的版本號(hào)。按約定,如果協(xié)議改變后與以前的相兼容,主協(xié)議版本號(hào)不變;如果不相兼容,則主主協(xié)議版本號(hào)升高。
服務(wù)器接到客戶端送來的協(xié)議版本號(hào)后,把它與自己的進(jìn)行比較,決定能否與客戶端一起工作。如果不能,則斷開TCP 連接;如果能,則按照二進(jìn)制數(shù)據(jù)包協(xié)議發(fā)送第一個(gè)二進(jìn)制數(shù)據(jù)包,雙方以較低的協(xié)議版本來一起工作。到此為止,這兩個(gè)報(bào)文只是簡(jiǎn)單的字符串,你我等凡人直接可讀。
第二階段:
協(xié)商解決版本問題后,雙方就開始采用二進(jìn)制數(shù)據(jù)包進(jìn)行通訊。由服務(wù)器向客戶端發(fā)送第一個(gè)包,內(nèi)容為自己的 RSA主機(jī)密鑰(host key)的公鑰部分、RSA服務(wù)密鑰(server key)的公鑰部分、支持的加密方法、支持的認(rèn)證方法、次協(xié)議版本標(biāo)志、以及一個(gè) 64 位的隨機(jī)數(shù)(cookie)。這個(gè)包沒有加密,是明文發(fā)送的。客戶端接收包后,依據(jù)這兩把密鑰和被稱為cookie的 64 位隨機(jī)數(shù)計(jì)算出會(huì)話號(hào)(session id)和用于加密的會(huì)話密鑰(session key)。隨后客戶端回送一個(gè)包給服務(wù)器,內(nèi)容為選用的加密方法、cookie的拷貝、客戶端次協(xié)議版本標(biāo)志、以及用服務(wù)器的主機(jī)密鑰的公鑰部分和服務(wù)密鑰的公鑰部分進(jìn)行加密的用于服務(wù)器計(jì)算會(huì)話密鑰的32 字節(jié)隨機(jī)字串。除這個(gè)用于服務(wù)器計(jì)算會(huì)話密鑰的 32字節(jié)隨機(jī)字串外,這個(gè)包的其他內(nèi)容都沒有加密。之后,雙方的通訊就是加密的了,服務(wù)器向客戶端發(fā)第二個(gè)包(雙方通訊中的第一個(gè)加密的包)證實(shí)客戶端的包已收到。
第三階段:
雙方隨后進(jìn)入認(rèn)證階段??梢赃x用的認(rèn)證的方法有:
(1) ~/.rhosts 或 /etc/hosts.equiv 認(rèn)證(缺省配置時(shí)不容許使用它);
(2) 用 RSA 改進(jìn)的 ~/.rhosts 或 /etc/hosts.equiv 認(rèn)證;
(3) RSA 認(rèn)證;
(4) 口令認(rèn)證。
如果是使用 ~/.rhosts 或 /etc/hosts.equiv 進(jìn)行認(rèn)證,客戶端使用的端口號(hào)必須小于1024。
認(rèn)證的第一步是客戶端向服務(wù)器發(fā) SSH_CMSG_USER 包聲明用戶名,服務(wù)器檢查該用戶是否存在,確定是否需要進(jìn)行認(rèn)證。如果用戶存在,并且不需要認(rèn)證,服務(wù)器回送一個(gè)SSH_SMSG_SUCCESS 包,認(rèn)證完成。否則,服務(wù)器會(huì)送一個(gè) SSH_SMSG_FAILURE 包,表示或是用戶不存在,或是需要進(jìn)行認(rèn)證。注意,如果用戶不存在,服務(wù)器仍然保持讀取從客戶端發(fā)來的任何包。除了對(duì)類型為 SSH_MSG_DISCONNECT、SSH_MSG_IGNORE 以及 SSH_MSG_DEBUG 的包外,對(duì)任何類型的包都以 SSH_SMSG_FAILURE 包。用這種方式,客戶端無法確定用戶究竟是否存在。
如果用戶存在但需要進(jìn)行認(rèn)證,進(jìn)入認(rèn)證的第二步??蛻舳私拥椒?wù)器發(fā)來的 SSH_SMSG_FAILURE 包后,不停地向服務(wù)器發(fā)包申請(qǐng)用各種不同的方法進(jìn)行認(rèn)證,直到時(shí)限已到服務(wù)器關(guān)閉連接為止。時(shí)限一般設(shè)定為 5 分鐘。對(duì)任何一個(gè)申請(qǐng),如果服務(wù)器接受,就以 SSH_SMSG_SUCCESS 包回應(yīng);如果不接受,或者是無法識(shí)別,則以 SSH_SMSG_FAILURE 包回應(yīng)。
第四階段:
認(rèn)證完成后,客戶端向服務(wù)器提交會(huì)話請(qǐng)求。服務(wù)器則進(jìn)行等待,處理客戶端的請(qǐng)求。在這個(gè)階段,無論什么請(qǐng)求只要成功處理了,服務(wù)器都向客戶端回應(yīng) SSH_SMSG_SUCCESS包;否則回應(yīng) SSH_SMSG_FAILURE 包,這表示或者是服務(wù)器處理請(qǐng)求失敗,或者是不能識(shí)別請(qǐng)求。會(huì)話請(qǐng)求分為這樣幾類:申請(qǐng)對(duì)數(shù)據(jù)傳送進(jìn)行壓縮、申請(qǐng)偽終端、啟動(dòng) X11、TCP/IP 端口轉(zhuǎn)發(fā)、啟動(dòng)認(rèn)證代理、運(yùn)行 shell、執(zhí)行命令。到此為止,前面所有的報(bào)文都要求 IP 的服務(wù)類型(TOS)使用選項(xiàng) IPTOS_THROUGHPUT。
第五階段:
會(huì)話申請(qǐng)成功后,連接進(jìn)入交互會(huì)話模式。在這個(gè)模式下,數(shù)據(jù)在兩個(gè)方向上雙向傳送。此時(shí),要求 IP 的服務(wù)類型(TOS)使用 IPTOS_LOWDELAY 選項(xiàng)。當(dāng)服務(wù)器告知客戶端自己的退出狀態(tài)時(shí),交互會(huì)話模式結(jié)束。
(注意:進(jìn)入交互會(huì)話模式后,加密被關(guān)閉。在客戶端向服務(wù)器發(fā)送新的會(huì)話密鑰后,加密重新開始。用什么方法加密由客戶端決定。)
第二部分:數(shù)據(jù)包格式和加密類型
二進(jìn)制數(shù)據(jù)包協(xié)議:
包 = 包長(zhǎng)域(4字節(jié):u_int32_t) + 填充墊(1-7字節(jié))
+ 包類型域(1字節(jié):u_char) + 數(shù)據(jù)域
+ 校驗(yàn)和域(4字節(jié))
加密部分 = 填充墊 + 包類型 + 數(shù)據(jù) + 校驗(yàn)和
包長(zhǎng) = 1(包類型) + 數(shù)據(jù)字節(jié)長(zhǎng)度 + 4(校驗(yàn)和)
數(shù)據(jù)包壓縮:
如果支持壓縮,包類型域和數(shù)據(jù)域用 gzip 壓縮算法進(jìn)行壓縮。壓縮時(shí)在兩個(gè)數(shù)據(jù)傳送方向的任何一個(gè)上,包的壓縮部分(類型域+數(shù)據(jù)域)被構(gòu)造得象是它連在一起,形成一個(gè)連續(xù)的數(shù)據(jù)流。在兩個(gè)數(shù)據(jù)傳送方向上,壓縮是獨(dú)立進(jìn)行的。
數(shù)據(jù)包加密:
現(xiàn)時(shí)支持的數(shù)據(jù)加密方法有這樣幾種:
SSH_CIPHER_NONE 0 不進(jìn)行加密
SSH_CIPHER_IDEA 1 IDEA 加密法(CFB模式)
SSH_CIPHER_DES 2 DES 加密法(CBC模式)
SSH_CIPHER_3DES 3 3DES 加密法(CBC模式)
SSH_CIPHER_ARCFOUR 5 Arcfour加密法)
SSH_CIPHER_BLOWFISH 6 Blowfish 加密法
協(xié)議的所有具體實(shí)現(xiàn)都要求支持3DES。
DES 加密:
從會(huì)話密鑰中取前8個(gè)字節(jié),每個(gè)字只用高7位,忽略最低位,這樣構(gòu)成56位的密鑰供加密使用。加密時(shí)使用CBC 模式,初使矢量被初始化為全零。
3DES 加密:
3DES 是 DES 的變體,它三次獨(dú)立地使用 CBC 模式的DES 加密法,每一次的初始矢量都是獨(dú)立的。第一次用DES 加密法對(duì)數(shù)據(jù)進(jìn)行加密;第二次對(duì)第一次加密的結(jié)果用 DES 加密法進(jìn)行解密;第三次再對(duì)第二次解密的
結(jié)用 DES 加密法進(jìn)行加密。注意:第二次解密的結(jié)果并不就是被加密的數(shù)據(jù),因?yàn)槿问褂玫拿荑€和初始矢量都是分別不同的。與上面的 DES 加密采用的方法類似,第一次從會(huì)話密鑰中取起始的前8個(gè)字節(jié)生成加密密鑰,第二次取下一個(gè)緊跟著的8個(gè)字節(jié),第三次取再下一個(gè)緊跟著的8個(gè)字節(jié)。三次使用的初始矢量都初始化為零。
IDEA 加密:
加密密鑰取自會(huì)話密鑰的前16個(gè)字節(jié),使用 CFB 模式。初始矢量初始化為全零。
RC4 加密:
會(huì)話密鑰的前16個(gè)字節(jié)被服務(wù)器用作加密密鑰,緊接著的下一個(gè)16字節(jié)被客戶端用作加密密鑰。結(jié)果是兩個(gè)數(shù)據(jù)流方向上有兩個(gè)獨(dú)立的129位密鑰。這種加密算法非??臁?br>第二部分:密鑰的交換和加密的啟動(dòng)
在服務(wù)器端有一個(gè)主機(jī)密鑰文件,它的內(nèi)容構(gòu)成是這樣的:
1. 私鑰文件格式版本字符串;
2. 加密類型(1 個(gè)字節(jié));
3. 保留字(4 個(gè)字節(jié));
4. 4 個(gè)字節(jié)的無符號(hào)整數(shù);
5. mp 型整數(shù);
6. mp 型整數(shù);
7. 注解字符串的長(zhǎng)度;
8. 注解字符串;
9. 校驗(yàn)字(4 個(gè)字節(jié));
10. mp 型整數(shù);
11. mp 型整數(shù);
12. mp 型整數(shù);
13. mp 型整數(shù);
其中 4、5、6 三個(gè)字段構(gòu)成主機(jī)密鑰的公鑰部分;10、11、12、13 四個(gè)字段構(gòu)成主機(jī)密鑰的私鑰部分。9、10、11、12、13 五個(gè)字段用字段 2 的加密類型標(biāo)記的加密方法進(jìn)行了加密。4 個(gè)字節(jié)的校驗(yàn)字交叉相等,即第一個(gè)字節(jié)與第三個(gè)字節(jié)相等,第二個(gè)字節(jié)與第四個(gè)字節(jié)相等。在服務(wù)器讀取這個(gè)文件時(shí)進(jìn)行這種交叉相等檢查,如果不滿足這個(gè)條件,則報(bào)錯(cuò)退出。
服務(wù)器程序運(yùn)行的第一步,就是按照上面的字段劃分讀取主機(jī)密鑰文件。隨后生成一個(gè)隨機(jī)數(shù),再調(diào)用函數(shù)
void rsa_generate_key
(
RSAPrivateKey *prv,
RSAPublicKey *pub,
RandomState *state,
unsigned int bits
);
生成服務(wù)密鑰,服務(wù)密鑰也由公鑰和私鑰兩部分組成。上面的這個(gè)函數(shù)第一個(gè)指針參數(shù)指向服務(wù)密鑰的私鑰部分,第二個(gè)指向公鑰部分。然后把主機(jī)密鑰的公鑰部分和服務(wù)密鑰的公鑰部分發(fā)送給客戶端。在等到客戶端回應(yīng)的包后,服務(wù)器用自己的主機(jī)密鑰的私鑰部分和服務(wù)密鑰的私鑰部分解密得到客戶端發(fā)來的 32 字節(jié)隨機(jī)字串。然后計(jì)算自己的會(huì)話號(hào),并用會(huì)話號(hào)的前 16字節(jié) xor 客戶端發(fā)來的 32 字節(jié)隨機(jī)字串的前 16 字節(jié),把它作為自己的會(huì)話密鑰。注意,服務(wù)器把8個(gè)字節(jié)的 cookie、主機(jī)密鑰的公鑰部分、和服務(wù)密鑰的公鑰部分作為參數(shù)來計(jì)算自己的會(huì)話號(hào)。
再來看客戶端。客戶端啟動(dòng)后的第一步驟也是讀取主機(jī)密鑰。然后等待服務(wù)器主機(jī)密鑰、服務(wù)密鑰、和 8個(gè)字節(jié)的cookie。注意,服務(wù)器發(fā)送來的只是主機(jī)密鑰和服務(wù)密鑰的公鑰部分。接到包后,客戶端立即把從服務(wù)器端收到cookie、主機(jī)密鑰、和服務(wù)密鑰作為參數(shù)計(jì)算出會(huì)話號(hào)。從上面可以看出,服務(wù)器和客戶端各自計(jì)算出的會(huì)話號(hào)實(shí)際是一樣的。
隨后,客戶端檢查用戶主機(jī)列表和系統(tǒng)主機(jī)列表,查看從服務(wù)器收到的主機(jī)密鑰是否在列表中。如果不在列表中,則把它加入列表中。然后就生成 32 字節(jié)的隨機(jī)字串,這個(gè)32 字節(jié)的隨機(jī)字串就是客戶端的會(huì)話密鑰??蛻舳擞?16字節(jié)的會(huì)話密鑰 xor 它的前 16 字節(jié),把結(jié)果用服務(wù)器的主機(jī)密鑰和服務(wù)密鑰進(jìn)行雙重加密后發(fā)送給服務(wù)器。產(chǎn)生 32字節(jié)隨機(jī)字串時(shí),隨機(jī)數(shù)種子由兩部分組成,其中一部分從系統(tǒng)隨機(jī)數(shù)種子文件中得到,這樣來避免會(huì)話密鑰被猜出。從上面服務(wù)器和客戶端各自計(jì)算會(huì)話密鑰的過程可以看出,服務(wù)器和客戶端計(jì)算出的會(huì)話密鑰是一樣的。
上面的這幾步,總結(jié)起來就要交換確定會(huì)話密鑰,因?yàn)闊o論是 des、idea、3des、arcfour、還是 blowfish 都是對(duì)稱加密方法,只有一把密鑰,雙方都知道了會(huì)話密鑰才能啟動(dòng)加密。但會(huì)話密鑰不能在網(wǎng)絡(luò)上明文傳送,否則加密就失去意義了。于是使用 RSA 公鑰體系對(duì)會(huì)話密鑰進(jìn)行加密。
RSA 公鑰體系的辦法是用公鑰加密私鑰解密,它依據(jù)這樣的數(shù)學(xué)定理:
若 p、q 是相異的兩個(gè)質(zhì)數(shù),整數(shù) r 和 m 滿足
rm == 1 (mod (p-1)(q-1))
a 是任意的整數(shù),整數(shù) b、c 滿足 b == a^m (mod pq),
c == b^r (mod pq)。則
c == a (mod pq)。
具體實(shí)現(xiàn)是這樣的:
(1) 找三個(gè)正整數(shù) p、q、r,其中 p、q 是相異的質(zhì)數(shù),
r 是與(p-1)、(q-1)互質(zhì)的數(shù)。這三個(gè)數(shù) p、q、r
就是私鑰(private key)。
(2) 再找一個(gè)正整數(shù) m 滿足 rm == 1 (mod(p-1)(q-1))。
計(jì)算 n = pq,m、n 就是公鑰(public key)。
(3) 被加密對(duì)象 a 看成是正整數(shù),設(shè) a < n。若 a >= n,
將 a 表示成 s (s < n,通常取 s = 2^t) 進(jìn)制的,
然后對(duì)每一位分別編碼。
(4) 加密:計(jì)算 b == a^m (mod n) (0 <= b < n),b 為
加密結(jié)果。
(5) 解密:計(jì)算 c == b^r (mod n) (0 <= c < n),c 為
解密結(jié)果。
從上面的數(shù)學(xué)定理可知,最后結(jié)果 c = a。
計(jì)算 RSA 密鑰的方法及過程是,調(diào)用下面的函數(shù)計(jì)算 RSA公鑰和 RSA 私鑰:
_______________________________________________________
void rsa_generate_key
(
RSAPrivateKey *prv, RSAPublicKey *pub,
RandomState *state, unsigned int bits
)
{
MP_INT test, aux;
unsigned int pbits, qbits;
int ret;
mpz_init(&prv->q);
mpz_init(&prv->p);
mpz_init(&prv->e);
mpz_init(&prv->d);
mpz_init(&prv->u);
mpz_init(&prv->n);
mpz_init(&test);
mpz_init(&aux);
/* 計(jì)算質(zhì)數(shù) p、q 的位數(shù) */
pbits = bits / 2;
qbits = bits - pbits;
retry0:
fprintf(stderr, "Generating p: ");
/* 生成隨機(jī)質(zhì)數(shù) p */
rsa_random_prime(&prv->p, state, pbits);
retry:
fprintf(stderr, "Generating q: ");
/* 生成隨機(jī)質(zhì)數(shù) q */
rsa_random_prime(&prv->q, state, qbits);
/* 判斷是否 p == q,如果是返回重新生成 */
ret = mpz_cmp(&prv->p, &prv->q);
if (ret == 0)
{
fprintf(stderr,
"Generated the same prime twice!\n");
goto retry;
}
if (ret > 0)
{
mpz_set(&aux, &prv->p);
mpz_set(&prv->p, &prv->q);
mpz_set(&prv->q, &aux);
}
/* 確定 p、q 是否很接近 */
mpz_sub(&aux, &prv->q, &prv->p);
mpz_div_2exp(&test, &prv->q, 10);
if (mpz_cmp(&aux, &test) < 0)
{
fprintf(stderr,
"The primes are too close together.\n");
goto retry;
}
/* Make certain p and q are relatively prime (in case
one or both were false positives... Though this is
quite impossible). */
mpz_gcd(&aux, &prv->p, &prv->q);
if (mpz_cmp_ui(&aux, 1) != 0)
{
fprintf(stderr,
"The primes are not relatively prime!\n");
goto retry;
}
/* 從質(zhì)數(shù) p、q 導(dǎo)出私鑰 */
fprintf(stderr, "Computing the keys...\n");
derive_rsa_keys(&prv->n, &prv->e, &prv->d,
&prv->u, &prv->p, &prv->q, 5);
prv->bits = bits;
/* 從質(zhì)數(shù) p、q 導(dǎo)出公鑰 */
pub->bits = bits;
mpz_init_set(&pub->n, &prv->n);
mpz_init_set(&pub->e, &prv->e);
/* 測(cè)試公鑰和密鑰是否有效 */
fprintf(stderr, "Testing the keys...\n");
rsa_random_integer(&test, state, bits);
mpz_mod(&test, &test, &pub->n); /* must be less than n. */
rsa_private(&aux, &test, prv);
rsa_public(&aux, &aux, pub);
if (mpz_cmp(&aux, &test) != 0)
{
fprintf(stderr,
"**** private+public failed to decrypt.\n");
goto retry0;
}
rsa_public(&aux, &test, pub);
rsa_private(&aux, &aux, prv);
if (mpz_cmp(&aux, &test) != 0)
{
fprintf(stderr,
"**** public+private failed to decrypt.\n");
goto retry0;
}
mpz_clear(&aux);
mpz_clear(&test);
fprintf(stderr, "Key generation complete.\n");
}
_______________________________________________________
在上面的函數(shù)成一對(duì)密鑰時(shí),首先調(diào)用函數(shù)
_______________________________________________________
void rsa_random_prime
(
MP_INT *ret, RandomState *state,
unsigned int bits
)
{
MP_INT start, aux;
unsigned int num_primes;
int *moduli;
long difference;
mpz_init(&start);
mpz_init(&aux);
retry:
/* 挑出一個(gè)隨機(jī)的足夠大的整數(shù) */
rsa_random_integer(&start, state, bits);
/* 設(shè)置最高的兩位 */
mpz_set_ui(&aux, 3);
mpz_mul_2exp(&aux, &aux, bits - 2);
mpz_ior(&start, &start, &aux);
/* 設(shè)置最低的兩位為奇數(shù) */
mpz_set_ui(&aux, 1);
mpz_ior(&start, &start, &aux);
/* 啟動(dòng)小質(zhì)數(shù)的 moduli 數(shù) */
moduli = malloc(MAX_PRIMES_IN_TABLE * sizeof(moduli[0]));
if (moduli == NULL)
{
printf(stderr, "Cann't get memory for moduli\n");
exit(1);
}
if (bits < 16)
num_primes = 0;
/* Don\'t use the table for very small numbers. */
else
{
for (num_primes = 0;
small_primes[num_primes] != 0; num_primes++)
{
mpz_mod_ui(&aux, &start, small_primes[num_primes]);
moduli[num_primes] = mpz_get_ui(&aux);
}
}
/* 尋找一個(gè)數(shù),它不能被小質(zhì)數(shù)整除 */
for (difference = 0; ; difference += 2)
{
unsigned int i;
if (difference > 0x70000000)
{
fprintf(stderr, "rsa_random_prime: "
"failed to find a prime, retrying.\n");
if (moduli != NULL)
free(moduli);
else
exit(1);
goto retry;
}
/* 檢查它是否是小質(zhì)數(shù)的乘積 */
for (i = 0; i < num_primes; i++)
{
while (moduli[i] + difference >= small_primes[i])
moduli[i] -= small_primes[i];
if (moduli[i] + difference == 0)
break;
}
if (i < num_primes)
continue; /* Multiple of a known prime. */
/* 檢查通過 */
fprintf(stderr, ".");
/* Compute the number in question. */
mpz_add_ui(ret, &start, difference);
/* Perform the fermat test for witness 2.
This means: it is not prime if 2^n mod n != 2. */
mpz_set_ui(&aux, 2);
mpz_powm(&aux, &aux, ret, ret);
if (mpz_cmp_ui(&aux, 2) == 0)
{
/* Passed the fermat test for witness 2. */
fprintf(stderr, "+");
/* Perform a more tests. These are probably unnecessary. */
if (mpz_probab_prime_p(ret, 20))
break; /* It is a prime with probability 1 - 2^-40. */
}
}
/* Found a (probable) prime. It is in ret. */
fprintf(stderr, "+ (distance %ld)\n", difference);
/* Free the small prime moduli; they are no longer needed. */
if (moduli != NULL)
free(moduli);
else
exit(1);
/* Sanity check: does it still have the high bit set (we might have
wrapped around)? */
mpz_div_2exp(&aux, ret, bits - 1);
if (mpz_get_ui(&aux) != 1)
{
fprintf(stderr,
"rsa_random_prime: high bit not set, retrying.\n");
goto retry;
}
mpz_clear(&start);
mpz_clear(&aux);
}
_______________________________________________________
隨機(jī)產(chǎn)生一對(duì)大質(zhì)數(shù)(p,q)。這對(duì)隨機(jī)大質(zhì)數(shù)要符合的條件是p 必須小于 q。然后調(diào)用下面的函數(shù)來生成公鑰和私鑰對(duì)的其他組員:
static void derive_rsa_keys
(
MP_INT *n, MP_INT *e, MP_INT *d, MP_INT *u,
MP_INT *p, MP_INT *q,
unsigned int ebits
)
{
MP_INT p_minus_1, q_minus_1, aux, phi, G, F;
assert(mpz_cmp(p, q) < 0);
mpz_init(&p_minus_1);
mpz_init(&q_minus_1);
mpz_init(&aux);
mpz_init(&phi);
mpz_init(&G);
mpz_init(&F);
/* 計(jì)算 p-1 和 q-1. */
mpz_sub_ui(&p_minus_1, p, 1);
mpz_sub_ui(&q_minus_1, q, 1);
/* phi = (p - 1) * (q - 1) */
mpz_mul(&phi, &p_minus_1, &q_minus_1);
/* G is the number of "spare key sets" for a given
modulus n. The smaller G is, the better. The
smallest G can get is 2. */
mpz_gcd(&G, &p_minus_1, &q_minus_1);
if (mpz_cmp_ui(&G, 100) >= 0)
{
fprintf(stderr, "Warning: G=");
mpz_out_str(stdout, 10, &G);
fprintf(stderr,
" is large (many spare key sets); key may be bad!\n");
}
/* F = phi / G; the number of relative prime
numbers per spare key set. */
mpz_div(&F, &phi, &G);
/* Find a suitable e (the public exponent). */
mpz_set_ui(e, 1);
mpz_mul_2exp(e, e, ebits);
mpz_sub_ui(e, e, 1); /*make lowest bit 1, and substract 2.*/
/* Keep adding 2 until it is relatively prime
to (p-1)(q-1). */
do
{
mpz_add_ui(e, e, 2);
mpz_gcd(&aux, e, &phi);
}
while (mpz_cmp_ui(&aux, 1) != 0);
/* d is the multiplicative inverse of e, mod F.
Could also be mod (p-1)(q-1); however, we try to
choose the smallest possible d. */
mpz_mod_inverse(d, e, &F);
/* u is the multiplicative inverse of p, mod q,
if p < q. It is used when doing private key
RSA operations using the chinese remainder
theorem method. */
mpz_mod_inverse(u, p, q);
/* n = p * q (the public modulus). */
mpz_mul(n, p, q);
/* Clear auxiliary variables. */
mpz_clear(&p_minus_1);
mpz_clear(&q_minus_1);
mpz_clear(&aux);
mpz_clear(&phi);
mpz_clear(&G);
mpz_clear(&F);
}
_______________________________________________________
最后為檢驗(yàn)所生成的一對(duì)密鑰的有效性,它調(diào)用下面的函數(shù)產(chǎn)生一個(gè)隨機(jī)整數(shù)。
_______________________________________________________
void rsa_random_integer(MP_INT *ret, RandomState *state,
unsigned int bits)
{
unsigned int bytes = (bits + 7) / 8;
char *str = xmalloc(bytes * 2 + 1);
unsigned int i;
/* 生成一個(gè)適當(dāng)大小的16進(jìn)制隨機(jī)數(shù),把它轉(zhuǎn)化成mp型整數(shù) */
for (i = 0; i < bytes; i++)
sprintf(str + 2 * i, "%02x", random_get_byte(state));
/* 轉(zhuǎn)化到內(nèi)部表示 */
if (mpz_set_str(ret, str, 16) < 0)
{
fprintf("Intenal error, mpz_set_str returned error");
exit(1);
}
/* Clear extra data. */
memset(str, 0, 2 * bytes);
if (str != NULL)
free(str);
else
exit(1);
/* Reduce it to the desired number of bits. */
mpz_mod_2exp(ret, ret, bits);
}
_______________________________________________________
服務(wù)密鑰生成后,服務(wù)器發(fā)送一個(gè)包把兩把密鑰發(fā)送給客戶端,一個(gè)是主機(jī)密鑰的公鑰,另一個(gè)是服務(wù)密鑰的公鑰。跟隨這個(gè)包一起發(fā)送的還有服務(wù)器支持的加密類型和8個(gè)字節(jié)即64位的隨機(jī)字串 cookie。客戶端依據(jù)這兩把密鑰計(jì)算會(huì)話號(hào),會(huì)話號(hào)長(zhǎng)16字節(jié)即128位。計(jì)算方法是:
會(huì)話號(hào) = MD5(主機(jī)公鑰模數(shù) n || 服務(wù)公鑰模數(shù) n || cookie)
計(jì)算函數(shù)是:
void compute_session_id
(
unsigned char session_id[16],
unsigned char cookie[8],
unsigned int host_key_bits,
MP_INT *host_key_n,
unsigned int session_key_bits,
MP_INT *session_key_n
)
{
unsigned int bytes = (host_key_bits + 7) / 8 +
(session_key_bits + 7) / 8 + 8;
unsigned char *buf = xmalloc(bytes);
struct MD5Context md;
mp_linearize_msb_first(buf, (host_key_bits + 7 ) / 8, host_key_n);
mp_linearize_msb_first(buf + (host_key_bits + 7 ) / 8,
(session_key_bits + 7) / 8, session_key_n);
memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8,
cookie, 8);
MD5Init(&md);
MD5Update(&md, buf, bytes);
MD5Final(session_id, &md);
xfree(buf);
}
void mp_linearize_msb_first
(
unsigned char *buf, unsigned int len,
MP_INT *value
)
{
unsigned int i;
MP_INT aux;
mpz_init_set(&aux, value);
for (i = len; i >= 4; i -= 4)
{
unsigned long limb = mpz_get_ui(&aux);
PUT_32BIT(buf + i - 4, limb);
mpz_div_2exp(&aux, &aux, 32);
}
for (; i > 0; i--)
{
buf[i - 1] = mpz_get_ui(&aux);
mpz_div_2exp(&aux, &aux, 8);
}
mpz_clear(&aux);
}
隨后客戶端計(jì)算會(huì)話密鑰,計(jì)算過程是首先生成32個(gè)字節(jié)即256位隨機(jī)字串:
for (i = 0; i < 32; i++)
session_key[i] = random_get_byte(state);
然后用16字節(jié)的會(huì)話號(hào) xor 這32字的隨機(jī)字串的前16字節(jié),并安 msb 次序來排列構(gòu)成一個(gè)MP型整數(shù):
mpz_init_set_ui(&key, 0);
for (i = 0; i < 32; i++)
{
mpz_mul_2exp(&key, &key, 8);
if (i < 16)
mpz_add_ui(&key,&key, session_key[i]^session_id[i]);
else
mpz_add_ui(&key,&key, session_key[i]);
}
把結(jié)果發(fā)給服務(wù)器。在用服務(wù)器發(fā)來主機(jī)公鑰和服務(wù)公鑰對(duì)這個(gè)MP型整數(shù)作兩次 RSA 加密后,客戶端發(fā)一個(gè)包把這個(gè)MP型整數(shù)交給服務(wù)器。跟隨這個(gè)包一起還有客戶端選定的加密類型。注意,在客戶端,它用上面最初的32字節(jié)隨機(jī)串 session_key 來作為會(huì)話密鑰進(jìn)行加密,而不是發(fā)給服務(wù)器的會(huì)話密鑰 key。服務(wù)器接到上面MP型整數(shù)后,把它轉(zhuǎn)換成32字節(jié)即256位的字串。再用自己計(jì)算出的16字節(jié)的會(huì)話號(hào)xor 這個(gè)字串的前16字節(jié),把結(jié)果作為會(huì)話密鑰。服務(wù)器計(jì)算自己的16字節(jié)會(huì)話號(hào)時(shí)也是把發(fā)給客戶端的主機(jī)公鑰、服務(wù)公鑰、和16字節(jié)隨機(jī)串 cookie 作為輸入,因此它計(jì)算出的會(huì)話號(hào)與客戶端計(jì)算出的一樣。
在這之后,所有的數(shù)據(jù)傳輸都用選用客戶端指定的加密方法進(jìn)行加密了,加密時(shí)使用上面的會(huì)話密鑰。加密使用的代碼在 arcfour.c、des.c、idea.c、blowfish.c 中。
ssh 聲稱避免了 IP 欺騙,使用的方法在上面的密鑰交換中服務(wù)器給客戶端發(fā)了一個(gè)64位 cookie,要求客戶端原樣拷貝送回??床怀鲞@能避免 IP 欺騙。
第三部分:認(rèn)證
RSA公鑰和RSA私鑰數(shù)據(jù)結(jié)構(gòu)為:
typedef struct
{
unsigned int bits; /* 模數(shù)大小 */
MP_INT e; /* 公鑰指數(shù) */
MP_INT n; /* 模數(shù) */
} RSAPublicKey;
typedef struct
{
unsigned int bits; /* 模數(shù)大小 */
MP_INT n; /* 模數(shù) */
MP_INT e; /* 公鑰指數(shù) */
MP_INT d; /* 私鑰指數(shù) */
MP_INT u; /* Multiplicative inverse of p mod q. */
MP_INT p; /* 質(zhì)數(shù) p */
MP_INT q; /* 質(zhì)數(shù) q */
} RSAPrivateKey;
RSA 認(rèn)證的過程是,客戶端向服務(wù)器提交自己 RSA公鑰的模數(shù)成員,服務(wù)器先讀取用戶 .ssh 目錄中的公鑰文件進(jìn)行有效性檢驗(yàn),再生成一個(gè) 256 位二進(jìn)制隨機(jī)數(shù) cookie。隨后把這個(gè)隨機(jī)數(shù) cookie 用從公鑰文件讀出的公鑰加密后傳給客戶端,客戶端接到 cookie 后,先用自己的私鑰解密,再對(duì)這個(gè) cookie 和會(huì)話號(hào)計(jì)算出 16 字節(jié)的 md5水印,把兩個(gè)水印相加后發(fā)給服務(wù)器。服務(wù)器把它收到 md5水印和它自己對(duì) cookie 和會(huì)話號(hào)計(jì)算出的水印和進(jìn)行比較,如果相等,則認(rèn)證通過。
第四部分:shell 和 X11 調(diào)用
ssh 提供的一個(gè)重要功能就是 X 轉(zhuǎn)發(fā)功能,它可以在客戶端的顯示屏上把服務(wù)器端 X 程序的運(yùn)行結(jié)果以圖形形式顯示出來顯示在客戶端的顯示屏幕上。例如運(yùn)行 xterm 程序啟動(dòng)一個(gè) X 終端,該 X 終端窗口顯示在客戶端的顯示屏上。
先來看看 X 窗口系統(tǒng)本身的情況。X 窗口系統(tǒng)是 UNIX的圖形用戶界面(GUI),它采用"客戶/服務(wù)器"模式,二者之間的通訊遵從 X 協(xié)議。每臺(tái)主機(jī)運(yùn)行一個(gè) X 服務(wù)器,且只能運(yùn)行一個(gè) X 服務(wù)器,但一個(gè) X 服務(wù)器可以控制多個(gè)顯示屏幕(顯示器)。應(yīng)用程序要想進(jìn)行圖形顯示必須以客戶的方式向 X 服務(wù)器提交顯示請(qǐng)求,由 X 服務(wù)器統(tǒng)一控制進(jìn)行顯示。用戶運(yùn)行 X 程序時(shí),實(shí)際是調(diào)用 XOpenDisplay 庫(kù)函數(shù)打開一個(gè) PF_UNIX 或 TCP socket 連接到 X 服務(wù)器,然后通過這個(gè)連接向它提交顯示請(qǐng)求。連接建立后, X 客戶所做的第一件事就是:按用戶的 $DISPLAY 環(huán)境變量的值讀取用戶配置文件 .Xauthority 中的顯示記錄,把這條記錄的有關(guān)內(nèi)容提交給 X 服務(wù)器進(jìn)行認(rèn)證。如果認(rèn)證通過,就可以提交顯示請(qǐng)求了,這個(gè)過程稱為打開一個(gè) X 顯示。作為客戶的 X 程序在提交顯示請(qǐng)求時(shí),實(shí)際上是把 X 顯示數(shù)據(jù)寫入上面打開的 socket。在打開 X 顯示時(shí),必須提供協(xié)議號(hào)、認(rèn)證鑰(hexkey)、和屏幕號(hào),如果 X 服務(wù)器不是在本地運(yùn)行,還需要提供運(yùn)行 X 服務(wù)器的遠(yuǎn)程主機(jī)名。這些都記錄在用戶配置文件 .Xauthority 中,所給的協(xié)議號(hào)、認(rèn)證鑰、和屏幕號(hào)從這個(gè)列表中取出。可以用 xauth 命令來查看顯示列表里的內(nèi)容:
[wangdb@ /home/wangdb]> /usr/openwin/bin/xauth list
***.***.***/unix:10 MIT-MAGIC-COOKIE-1 92b404e556588ced6c1acd4ebf053f68
***.***.***/unix:11 MIT-MAGIC-COOKIE-1 92b404e556588ced6c1acd4ebf053f68
***.***.***:10 MIT-MAGIC-COOKIE-1 92b404e556588ced6c1acd4ebf053f68
***.***.***/unix:10 MIT-MAGIC-COOKIE-1 92b404e556588ced6c1acd4ebf053f68
***.***.***:11 MIT-MAGIC-COOKIE-1 92b404e556588ced6c1acd4ebf053f68
***.***.***/unix:11 MIT-MAGIC-COOKIE-1 92b404e556588ced6c1acd4ebf053f68
[wangdb@ /home/wangdb]> echo $DISPLAY
***.***.***:10.0
[wangdb@ /home/wangdb]> /usr/openwin/bin/xauth
Using authority file /home/wangdb/.Xauthority
xauth> list ***.***.***:10.0
***.***.***:10 MIT-MAGIC-COOKIE-1 92b404e556588ced6c1acd4ebf053f68
xauth> quit
[wangdb@ /home/wangdb]>
.Xauthority 文件的顯示記錄里各個(gè)字段的含義如下,第一個(gè)字段的***.***.*** 是主機(jī)名,":"號(hào)后的"."前面的數(shù)字是 X 服務(wù)器標(biāo)號(hào),"."后面的數(shù)字是顯示屏幕(顯示器)標(biāo)號(hào)。這個(gè)字段稱為顯示名,$DISPLAY 環(huán)境變量里填入這個(gè)字段。第二個(gè)字段是協(xié)議標(biāo)號(hào),第三個(gè)字段是十六進(jìn)制的認(rèn)證鑰。認(rèn)證鑰是由系統(tǒng)給的,打開 X 顯示時(shí)如果認(rèn)證鑰給的不對(duì),X 服務(wù)器拒絕處理顯示請(qǐng)求。
ssh 實(shí)現(xiàn) X 轉(zhuǎn)發(fā)的第一步是,客戶端調(diào)用 popen 函數(shù)執(zhí)行 "xauth list $DISPLAY" 命令,讀取 X 顯示的屏幕號(hào)、協(xié)議號(hào)、和認(rèn)證鑰,然后把協(xié)議號(hào)和認(rèn)證鑰保存在內(nèi)存中??蛻舳瞬⒉话炎约旱恼J(rèn)證鑰發(fā)送給服務(wù)器,而是生成一個(gè) 8位二進(jìn)制隨機(jī)數(shù)序列,以十六進(jìn)制打印,把這個(gè)十六進(jìn)制數(shù)字串發(fā)送給服務(wù)器作為認(rèn)證鑰。等到服務(wù)器發(fā)來打開 X 顯示請(qǐng)求時(shí),客戶端使用自己真正的認(rèn)證鑰打開 X 顯示。采用這種方法,客戶保證了自己的認(rèn)證鑰不會(huì)泄露給外界,安全性得到保證。
服務(wù)器接到客戶端的 X 轉(zhuǎn)發(fā)請(qǐng)求后,讀取客戶端發(fā)來的屏幕號(hào)、協(xié)議號(hào)、和認(rèn)證鑰,然后打開一個(gè) socket 并綁定它,設(shè)置成偵聽模式,并用這個(gè) socket 設(shè)置一個(gè)通道。隨后就從服務(wù)器自己的配置文件讀出 X 服務(wù)器標(biāo)號(hào),調(diào)用gethostname函數(shù)獲取本機(jī)主機(jī)名,把這兩者和客戶發(fā)來的屏幕號(hào)結(jié)合在一起構(gòu)成顯示列表記錄的第一字段。
在服務(wù)器處理客戶端執(zhí)行命令或啟動(dòng) shell 的請(qǐng)求時(shí),它用前面設(shè)置的通道接受一個(gè) TCP 連接,返回一個(gè) socket,再用這個(gè) socket 設(shè)置一個(gè)新通道。然后發(fā)一個(gè)包給客戶端要求它打開一個(gè) X 顯示。客戶端接到這個(gè)包后打開一個(gè)socket 與本地 X 服務(wù)器連接,即打開一個(gè) X 顯示:
_____________________________________________________
int display_number, sock;
const char *display;
struct sockaddr_un ssun;
/* Try to open a socket for the local X server. */
display = getenv("DISPLAY");
if (!display)
{
error("DISPLAY not set.");
goto fail;
}
/* Now we decode the value of the DISPLAY variable
* and make a connection to the real X server.
*/
/* Check if it is a unix domain socket. Unix domain
* displays are in one of the following formats:
* unix:d[.s], :d[.s], ::d[.s]
*/
if (strncmp(display, "unix:", 5) == 0 ||
display[0] == ':')
{
/* Connect to the unix domain socket. */
if (sscanf(strrchr(display, ':') + 1,
"%d", &display_number) != 1)
{
error("Could not parse display number "
"from DISPLAY: %.100s", display);
goto fail;
}
/* Create a socket. */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
{
error("socket: %.100s", strerror(errno));
goto fail;
}
/* Connect it to the display socket. */
ssun.sun_family = AF_UNIX;
#ifdef HPSUX_NONSTANDARD_X11_KLUDGE
{
/* HPSUX release 10.X uses
* /var/spool/sockets/X11/0
* for the unix-domain sockets, while earlier
* releases stores the socket in
* /usr/spool/sockets/X11/0
* with soft-link from
* /tmp/.X11-unix/`uname -n`0
*/
struct stat st;
if (stat("/var/spool/sockets/X11", &st) == 0)
{
sprintf(ssun.sun_path, "%s/%d",
"/var/spool/sockets/X11", display_number);
}
else
{
if (stat("/usr/spool/sockets/X11", &st) == 0)
{
sprintf(ssun.sun_path, "%s/%d",
"/usr/spool/sockets/X11", display_number);
}
else
{
struct utsname utsbuf;
/* HPSUX stores unix-domain sockets in
* /tmp/.X11-unix/`hostname`0
* instead of the normal /tmp/.X11-unix/X0.
*/
if (uname(&utsbuf) < 0)
fatal("uname: %.100s", strerror(errno));
sprintf(ssun.sun_path, "%.20s/%.64s%d",
X11_DIR, utsbuf.nodename, display_number);
}
}
}
#else /* HPSUX_NONSTANDARD_X11_KLUDGE */
{
struct stat st;
if (stat("/var/X", &st) == 0)
{
sprintf(ssun.sun_path, "%.80s/X%d",
"/var/X/.X11-unix", display_number);
}
else if (stat(X11_DIR, &st) == 0)
{
sprintf(ssun.sun_path, "%.80s/X%d",
X11_DIR, display_number);
}
else
{
sprintf(ssun.sun_path, "%.80s/X%d",
"/tmp/.X11-unix", display_number);
}
}
#endif /* HPSUX_NONSTANDARD_X11_KLUDGE */
if (connect(sock, (struct sockaddr *)&ssun,
AF_UNIX_SIZE(ssun)) < 0)
{
error("connect %.100s: %.100s",
ssun.sun_path, strerror(errno));
close(sock);
goto fail;
}
/* OK, we now have a connection to the display. */
goto success;
}
success:
/* We have successfully obtained a connection to
* the real X display.
*/
#if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN)
(void)fcntl(sock, F_SETFL, O_NONBLOCK);
#else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
(void)fcntl(sock, F_SETFL, O_NDELAY);
#endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
______________________________________________________
隨后客戶端用這個(gè) socket 設(shè)置一個(gè)新通道。注意,如果客戶端主機(jī)的本地沒有終端顯示器,在這一步,它也按自己的環(huán)境變量 $DISPLAY 的值,打開一個(gè) TCP socket 與遠(yuǎn)程 X服務(wù)器連接。
最后服務(wù)器把前面已經(jīng)構(gòu)造出的顯示列表記錄第一字段和客戶端發(fā)送來的協(xié)議號(hào)與認(rèn)證鑰結(jié)合在一起構(gòu)成一條顯示記錄,置入用戶的.Xauthority 文件中。并把 $DIAPLAY 環(huán)境變量的值設(shè)置為這條記錄第一個(gè)字段的顯示名。
做了這些之后,就可以進(jìn)行 X 轉(zhuǎn)發(fā)了。服務(wù)器運(yùn)行 X程序時(shí)使用這個(gè)虛擬的 X 顯示提交圖形顯示請(qǐng)求,把圖形顯示數(shù)據(jù)寫入這個(gè)虛擬的 X 顯示,也即寫入上面新建的通道發(fā)給客戶端。客戶端取得這些數(shù)據(jù)后再把它寫入自己剛剛建立的與 X 服務(wù)器連接的通道,也即向 X 服務(wù)器提交顯示請(qǐng)求。
為什么客戶端不直接把自己 .Xauthority 文件中一條顯示配置記錄交給服務(wù)器,由服務(wù)器按這條記錄直接打開 TCPsocket 與客戶端的 X 建立連接呢?ssh 的安全性也就在這里,如果這樣做,就把等于把自己的 X 服務(wù)器完全奉送給外界來使用,而 X 服務(wù)器本身又是問題多多的。前面?zhèn)卧煲粋€(gè)認(rèn)證鑰也是出于這個(gè)考慮,因?yàn)槿绻懒苏J(rèn)證鑰,顯示記錄里別的幾個(gè)字段是很容易猜出的。
盡管做了這些,還是存在問題的。如果一個(gè)攻擊者侵入或掌握著 ssh 服務(wù)器運(yùn)行的主機(jī),那么他/她發(fā)現(xiàn)一個(gè) ssh連接并進(jìn)行 X 轉(zhuǎn)發(fā)服務(wù)時(shí),設(shè)法獲取連接者的 $DISPLAY 環(huán)境變量值,再執(zhí)行一下 "xauth value_of_$DISPLAY" 命令,就得到顯示記錄了。隨后他/她用 "xauth add" 命令把這條記錄加入自己的 .Xauthority 文件中,再把自己的$DISPLAY環(huán)境變量設(shè)置成這條記錄的顯示名。這樣他/她就可以在 X轉(zhuǎn)發(fā)連接期間運(yùn)行 X 程序,X 程序的顯示請(qǐng)求全部提交給客戶端的 X 服務(wù)器了。如果 X 服務(wù)器有什么漏洞的話,他/她可以自由運(yùn)用了。

熱詞搜索:

上一篇:SSH:增強(qiáng)安全“免疫力”
下一篇:簡(jiǎn)易 Telnet 與 SSH 主機(jī)設(shè)定(1)

分享到: 收藏
国产一级一区二区_segui88久久综合9999_97久久夜色精品国产_欧美色网一区二区
美女91精品| 欧美四级在线观看| 亚洲午夜一区二区| 在线日韩欧美视频| 国产精品任我爽爆在线播放| 久久男人av资源网站| 亚洲在线视频观看| 亚洲精选成人| 亚洲国产人成综合网站| 韩国一区二区三区美女美女秀| 国产精品免费看久久久香蕉| 欧美精品粉嫩高潮一区二区| 欧美1区免费| 欧美va日韩va| 久热精品视频在线观看| 狂野欧美性猛交xxxx巴西| 久久精品一本| 久久美女性网| 久久爱www久久做| 久久久国际精品| 理论片一区二区在线| 免费在线日韩av| 欧美二区视频| 欧美日韩免费高清一区色橹橹| 欧美精品www| 欧美性生交xxxxx久久久| 国产精品亚洲片夜色在线| 国产日韩精品电影| 在线观看视频一区二区| 亚洲精品永久免费精品| 亚洲社区在线观看| 欧美一区二区精品久久911| 久久成人羞羞网站| 欧美 日韩 国产精品免费观看| 欧美91视频| 欧美视频一区二区三区在线观看| 国产精品视频一区二区高潮| 国产亚洲欧美一区二区| 亚洲国产毛片完整版 | 亚洲第一区中文99精品| 91久久久久久久久久久久久| 99re成人精品视频| 欧美在线不卡视频| 欧美顶级少妇做爰| 欧美另类亚洲| 欧美在线在线| 久久九九免费| 麻豆成人在线| 国产精品裸体一区二区三区| 国产婷婷色一区二区三区在线 | 久久高清国产| 欧美另类极品videosbest最新版本 | 久久久久国产成人精品亚洲午夜| 嫩草国产精品入口| 国产欧美一区二区三区视频| 亚洲国产三级| 久久精品人人做人人爽| 欧美日韩亚洲另类| 在线免费观看一区二区三区| 亚洲在线视频观看| 欧美日韩三级| 亚洲国产欧美日韩| 久久久国产精品一区二区三区| 欧美日韩国产精品自在自线| 激情五月婷婷综合| 欧美一区二区视频网站| 欧美午夜精品一区| 99国产精品国产精品毛片| 久久久午夜精品| 国产亚洲欧美在线| 性欧美xxxx大乳国产app| 欧美日韩中文字幕在线| 亚洲精品免费在线| 欧美jizz19性欧美| 91久久在线观看| 欧美一级在线视频| 国产精品视频网| 亚洲一区二区不卡免费| 欧美精品国产一区| 91久久精品美女| 欧美成人激情视频| 亚洲国产欧美日韩另类综合| 久久一二三四| 1204国产成人精品视频| 免费国产一区二区| 亚洲国产天堂久久综合网| 在线一区欧美| 欧美女同在线视频| 国产一区二区三区在线免费观看 | 暖暖成人免费视频| 国产综合一区二区| 久久精品99国产精品日本| 国产精品美女久久久久av超清 | 国产精品高潮久久| 宅男噜噜噜66一区二区66| 欧美成人视屏| 国产精品久久看| 亚洲国产精品一区在线观看不卡| 亚洲亚洲精品三区日韩精品在线视频| 蜜臀av一级做a爰片久久 | 国产欧亚日韩视频| 99天天综合性| 精品69视频一区二区三区| 日韩午夜激情电影| 欧美日本三级| 99www免费人成精品| 欧美国产一区二区在线观看| 亚洲丁香婷深爱综合| 久久夜色撩人精品| 在线观看国产欧美| 欧美大秀在线观看| 99精品欧美一区二区三区| 欧美日韩午夜| 篠田优中文在线播放第一区| 国产美女诱惑一区二区| 久久精品一区二区三区四区 | 久久精品视频在线免费观看| 国产一区二区三区四区三区四| 欧美在线综合| 欧美黄免费看| 日韩一级精品| 国产精品天美传媒入口| 久久精品国语| 亚洲日本中文字幕| 国产精品久久久久免费a∨大胸| 欧美亚洲一区在线| 亚洲国产精品一区二区尤物区| 欧美精品在线观看| 亚洲欧美在线免费观看| 亚洲丰满少妇videoshd| 欧美视频在线观看| 久久亚洲捆绑美女| 亚洲一区二区三区777| 伊人成人在线视频| 国产精品久久久久久亚洲毛片| 久久国产乱子精品免费女| 亚洲精品黄色| 欧美一级二区| 国产自产2019最新不卡| 久久se精品一区精品二区| 亚洲第一精品影视| 国产精品久久久对白| 久久综合网络一区二区| 中文国产成人精品久久一| 国产一区二区三区在线观看精品 | 在线欧美亚洲| 国产精品自在在线| 欧美久久一区| 久久国产直播| 亚洲天堂av在线免费观看| 亚洲国产福利在线| 国产一区二区福利| 国产精品乱码| 欧美日韩免费高清| 免费在线一区二区| 久久久久久欧美| 欧美一区二区三区另类 | 在线日韩精品视频| 欧美日韩精品三区| 欧美成人午夜视频| 久久久精彩视频| 欧美一区二区日韩一区二区| 99精品国产热久久91蜜凸| 精品999久久久| 国产日韩综合| 国产精品一区二区久久精品| 欧美日韩国产亚洲一区| 欧美国产一区二区| 男人的天堂成人在线| 久久久xxx| 久久国产精品久久久久久久久久| 中文在线资源观看网站视频免费不卡| 亚洲国产天堂网精品网站| 在线观看不卡av| 狠狠干成人综合网| 亚洲视频在线一区| 亚洲国产日韩欧美| 国产毛片精品视频| 国产精品久久久久三级| 欧美少妇一区| 欧美日韩成人一区二区三区| 女人色偷偷aa久久天堂| 免费成人高清| 欧美mv日韩mv国产网站| 欧美成人xxx| 欧美日韩免费区域视频在线观看| 欧美理论在线播放| 欧美日韩亚洲在线| 国产精品av久久久久久麻豆网| 欧美午夜视频在线| 国产乱人伦精品一区二区| 国产欧美一区二区三区久久| 国产午夜精品一区二区三区视频| 国产亚洲精品一区二区| 激情欧美一区| 亚洲日本中文字幕区| 中文欧美字幕免费| 欧美在线关看| 蜜臀a∨国产成人精品| 欧美日韩三级视频|