Diffie-Hellman金鑰交換演算法的原理及程式示範

來源:互聯網
上載者:User

在http://en.wikipedia.org/wiki/Diffie-Hellman上面給出了這個金鑰交換協議的曆史,原理,重要文獻的連結,以及示範代碼。它的數學基礎就是離散對數這個數學難題。用它進行金鑰交換的過程簡述如下:
選取兩個大數p和g並公開,其中p是一個素數,g是p的一個模p本原單位根(primitive root module p),所謂本原單位根就是指在模p乘法運算下,g的1次方,2次方……(p-1)次方這p-1個數互不相同,並且取遍1到p-1;
對於Alice(其中的一個通訊者),隨機產生一個整數a,a對外保密,計算Ka = g^a mod p,將Ka發送給Bob;
對於Bob(另一個通訊者),隨機產生一個整數b,b對外保密,計算Kb = g^b mod p,將Kb發送給Alice;
在Alice方面,收到Bob送來的Kb後,計算出密鑰為:key = Kb^a mod p=(g^b)^a=g^(b*a) mod p;
對於Bob,收到Alice送來的Ka後,計算出密鑰為:key = Ka ^ b mod p=(g^a)^b=g^(a*b) mod p。
攻擊者知道p和g,並且截獲了Ka和Kb,但是當它們都是非常大的數的時候,依靠這四個數來計算a和b非常困難,這就是離散對數數學難題。
要實現Diffie-Hellman金鑰交換協議,需要能夠快速計算大數模冪,在模冪演算法中,仍需計算大數的乘法和模運算,所以整個過程需要三個演算法:高精度乘法,高精度除法(用來同時求出一個大數除以另一個大數的商和餘數),快速模冪演算法。
高精度的乘法和除法可以程式類比手算。快速模冪演算法也是從手算中總結出規律來,例如:
5^8 = (5^2)^4 = (25)^4 = (25^2)^2 = (625)^2,這樣,原來計算5^8需要做8次乘法,而現在則只需要三次乘法,分別是:5^2, 25^2, 625^2。這就是快速模冪演算法的基礎。將演算法描述出來,那就是:
演算法M:輸入整數a,b,p,計算a^b mod p:
M1.初始化c = 1
M2.如果b為0, 則c就是所要計算的結果。返回c的值。演算法結束。
M3.如果b為奇數,則令c = c *a mod p, 令b = b-1,轉到M2。
M4.如果b為偶數,則令a = a * a mod p, 令b = b / 2,轉到M2。
高精度試除法原理簡單,但是代碼實現起來需要仔細考慮一些細節。
我的示範代碼如下:
高精度運算類:

  1. class SuperNumber {
  2. public:
  3.     SuperNumber() {
  4.         memset(data, 0, MAX_SIZE);
  5.         high = 0;
  6.     }
  7.     // 一般整型到SuperNumber的轉換,該版本中不支援負數
  8.     SuperNumber(unsigned long l) {
  9.         memset(data, 0, MAX_SIZE);
  10.         high = 0;
  11.         while(l) {
  12.             data[++high] = l % 10;
  13.             l /= 10;
  14.         }
  15.     }
  16.     // str為字串形式表示的十進位數
  17.     SuperNumber(const char* str) {
  18.         assert(str != NULL);
  19.         high = strlen(str);
  20.         for(int i = high, j = 0; i >= 1; i--, j++) {
  21.             data[i] = str[j] - '0';
  22.         }
  23.     }
  24.     SuperNumber(const SuperNumber& s) {
  25.         memcpy(data, s.data, MAX_SIZE);
  26.         high = s.high;
  27.     }
  28.     operator const char*() const {
  29.         return toString(10);
  30.     }
  31.     SuperNumber& operator=(const SuperNumber& s) {
  32.         if(this != &s) {
  33.             memcpy(data, s.data, MAX_SIZE);
  34.             high = s.high;
  35.         }
  36.         return *this;
  37.     }
  38.     // 將資料置為0
  39.     void reset() {
  40.         memset(data, 0, MAX_SIZE);
  41.         high = 0;
  42.     }
  43.     // str為字串形式表示的十進位數
  44.     void setToStr(const char* str) {
  45.         assert(str != NULL);
  46.         high = strlen(str);
  47.         for(int i = high, j = 0; i >= 1; i--, j++) {
  48.             data[i] = str[j] - '0';
  49.         }
  50.     }
  51.     // 將資料轉換成以base指定的進位的字串形式,預設為十進位
  52.     const char* toString(int base = 10) const {
  53.         static char buf[MAX_SIZE];
  54.         const char table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  55.         if(high == 0) return "0";
  56.         assert(base >= 2);      // 指定的進位應該不小於2
  57.         // 進位轉換
  58.         buf[MAX_SIZE-1] = '/0';
  59.         int begin = MAX_SIZE-1;
  60.         char temp[MAX_SIZE];
  61.         memcpy(temp, data, MAX_SIZE);
  62.         while(1) {
  63.             // 找最高位的起始位置
  64.             int h = high;
  65.             while(temp[h] == 0 && h >= 1) h--;
  66.             if(h == 0) break;
  67.             // 除基取餘
  68.             int t = 0;
  69.             while(h >= 1) {
  70.                 t = t * 10 + temp[h];
  71.                 temp[h] = t / base;
  72.                 t = t % base;
  73.                 h--;
  74.             }
  75.             buf[--begin] = table[t];
  76.         }
  77.         return buf+begin;
  78.     }
  79.     // 乘法
  80.     SuperNumber operator*(const SuperNumber& s) const {
  81.         SuperNumber result;     // default set to 0
  82.         int i, j;
  83.         
  84.         // 相乘
  85.         for(i = 1; i <= high; i++) {
  86.             for(j = 1; j <= s.high; j++) {
  87.                 int k = data[i] * s.data[j] + result.data[i+j-1];
  88.                 result.data[i+j-1] = k % 10;
  89.                 result.data[i+j] += k / 10;
  90.             }
  91.         }
  92.         // 進位
  93.         for(i = 1; i < MAX_SIZE - 1; i++) {
  94.             if(result.data[i] >= 10) {
  95.                 result.data[i+1] += result.data[i] / 10;
  96.                 result.data[i] %= 10;
  97.             }
  98.         }
  99.         // 確定最高位
  100.         for(i = MAX_SIZE-1; i >= 1 && result.data[i] == 0; i--);
  101.         result.high = i;
  102.         return result;
  103.     }
  104.     // 除法,內部調用doDivide來實現
  105.     SuperNumber operator/(const SuperNumber& s) const {
  106.         SuperNumber q, r;
  107.         doDivide(s, q, r);
  108.         return q;
  109.     }
  110.     // 模運算,內部調用doDivide來實現
  111.     SuperNumber operator%(const SuperNumber& s) const {
  112.         SuperNumber q, r;
  113.         doDivide(s, q, r);
  114.         return r;
  115.     }
  116.     // 除法運算,一次除法運算中同時得到商和餘數,運算子/和%的重載
  117.     // 內部會調用這個函數,dest為除數,Q為商,R為餘數,演算法使用試除法
  118.     void doDivide(const SuperNumber& dest, SuperNumber& Q, SuperNumber& R) const {
  119.         int i, j, t;
  120.         Q.reset();
  121.         Q.high = high - dest.high + 1; // 商的初始位元
  122.         R = *this;                     // 餘數初始實為被除數
  123.         t = dest.high;
  124.         // 判斷除法是否結束
  125.         while(R >= dest) {
  126.             // 迴圈相減進行試除
  127.             while(dest >= R.sub(1, t)) {
  128.                 Q.data[Q.high--] = 0;
  129.                 ++t;
  130.             }
  131.             while(R.sub(1, t) >= dest) {
  132.                 // i為相減時被除數最低下標,j為除數最低下標
  133.                 for(i=R.high-t+1,j=1; j<=dest.high; i++,j++) {
  134.                     R.data[i] -= dest.data[j];
  135.                     if(R.data[i] < 0) {
  136.                         R.data[i] += 10;
  137.                         R.data[i+1] -= 1;
  138.                     }
  139.                 }
  140.                 while(R.data[i] < 0 && i <= R.high) {
  141.                     R.data[i] += 10;
  142.                     R.data[i+1] -= 1;
  143.                     ++i;
  144.                 }
  145.                 Q.data[Q.high] += 1;
  146.             }
  147.             // 一次試除結束,更新商的最高位下標
  148.             Q.high -= 1;
  149.             // 更新被除數的最高位下標
  150.             while(R.data[R.high] == 0) {
  151.                 R.high--;
  152.                 t--;
  153.             }
  154.             t+=1;               // 下一位被除數
  155.         }
  156.         Q.high = high - dest.high + 1; 
  157.         while(Q.data[Q.high] == 0) Q.high -= 1;
  158.         R.high = high;
  159.         while(R.data[R.high] == 0) R.high -= 1;
  160.     }
  161.     // 大數模冪演算法,很簡單的自然演算法,即將指數分解為二進位,換句
  162.     // 更簡單的話來說,就是不斷地找平方模冪,而不是全部乘方後再
  163.     // 做一次最終的模運算
  164.     // a.power_mod(p, n)計算a^p mod n
  165.     SuperNumber power_mod(int power, SuperNumber n) const {
  166.         SuperNumber c("1"), t(*this);
  167.         while(power) {
  168.             if(power % 2) {
  169.                 c = c * t % n;
  170.                 power -= 1;
  171.             } else {
  172.                 t = t * t % n;
  173.                 power /= 2;
  174.             }
  175.         }
  176.         return c%n;
  177.     }
  178.     bool operator>=(const SuperNumber& s) const {
  179.         if(high == s.high) {
  180.             int k = high;
  181.             while(data[k] == s.data[k] && k >= 1)k--;
  182.             if(k < 1) return true; // equal
  183.             return data[k] > s.data[k];
  184.         } else if(high > s.high) return true;
  185.         return false;        
  186.     }
  187.     bool operator<(const SuperNumber& s) const {
  188.         return !(*this >= s);
  189.     }
  190.     // 從十進位表示的最高位開始數起,數到第i位,從第i位開始截取連續
  191.     // 的c位元字出來組成一個新的數。例如:資料是12345678925698,則
  192.     // sub(3, 5)返回數字34567,如果數字不夠取,例如在34567上運行
  193.     // sub(3, 5),因為34567從高位元起第3個數字是5,剩下的數字是567,
  194.     // 最多隻有三個,不夠取要求的5個,這個時候返回567,不報錯。
  195.     SuperNumber sub(int i, int c) const {
  196.         SuperNumber ret;
  197.         assert(high >= i);   // 保證可截取
  198.         i = high - i + 1;    // 從高位元起的第i個數位的下標
  199.         if(i >= c) {
  200.             ret.high = c;
  201.             while(c >= 1) ret.data[c--] = data[i--];
  202.         } else {
  203.             ret.high = i;
  204.             while(i >= 1) {
  205.                 ret.data[i] = data[i];
  206.                 i--;
  207.             }
  208.         } 
  209.         // 過濾前置0
  210.         while(ret.data[ret.high] == 0) ret.high--;
  211.         return ret;
  212.     }
  213.     // I/O
  214.     friend istream& operator>>(istream& in, SuperNumber& s) {
  215.         char t[256];
  216.         in >> t;
  217.         s.setToStr(t);
  218.         return in;
  219.     }
  220.     friend ostream& operator<<(ostream& out, const SuperNumber& s) {
  221.         return out << s.toString(10);
  222.     }
  223. private:
  224.     enum {MAX_SIZE=256};        // 最大十進位位元
  225.     // 須注意,使用data[0]儲存最高位所在下標是自己的一點小聰明,後來在
  226.     // 調試的時候發現這是一個極大的錯誤,不過對於此題目來說可以應付
  227.     char data[MAX_SIZE];        // 資料的內部表示,字串形式的十進位
  228.                                 // 其中data[0]儲存最高位所在下標,data[1]
  229.                                 // 儲存資料的最低位,也就是個位
  230.     int high;
  231. };

主函數:

  1. int main(int argc, char **argv) {
  2.     freopen("in.txt", "r", stdin);
  3.     SuperNumberTest st;
  4. //    st.run();
  5.     // g和n都是超過2^127的素數。它們在DH演算法中公開
  6.     SuperNumber g, n;
  7.     int a, b;
  8.     SuperNumber ka, kb, key;
  9.     srand(time(0));
  10.     cin >> g >> n;
  11.     cout << "g = " << g << endl
  12.          << "n = " << n << endl;
  13.     cout << "/nThis is Alice:/n";
  14.     a = rand();
  15.     cout << "Alice get a random integer a = " << a << endl;
  16.     cout << "Alice computer g^a mod n:/n";
  17.     ka = g.power_mod(a, n);
  18.     cout << "Alice compute out ka = " << ka << endl;
  19.     cout << "/nThis is Bob:/n";
  20.     b = rand();
  21.     cout << "Bob get a random integer b = " << b << endl;
  22.     cout << "Bob compute g^b mod n:/n";
  23.     kb = g.power_mod(b, n);
  24.     cout << "Bob compute out kb = " << kb << endl;
  25.     cout << "/nAlice get kb from Bob, she compute out key is:/n";
  26.     cout << kb.power_mod(a, n) << endl;
  27.     cout << "/nBob get ka from Alice, he compute out key is:/n";
  28.     cout << ka.power_mod(b, n) << endl;
  29.     return 0;
  30. }

運行結果:
g = 170141183460469231731687303715884105757
n = 170141183460469231731687303715884106309
 
This is Alice:
Alice get a random integer a = 20276
Alice computer g^a mod n:
Alice compute out ka = 102075421398841759242347870420481896337
 
This is Bob:
Bob get a random integer b = 28664
Bob compute g^b mod n:
Bob compute out kb = 62348451302684698452476840835428450852
 
Alice get kb from Bob, she compute out key is:
80402514625208456390620786920929643017
 
Bob get ka from Alice, he compute out key is:
80402514625208456390620786920929643017
在http://oldpiewiki.yoonkn.com/cgi-bin/moin.cgi/DiffieHellmanKeyExchange 上面給出了使用GNU gmp庫實現的這個演算法的示範代碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.