我發誓這真的是最後一篇關於ECDH的文兒!(API安全加強篇四)

首先是前段時間我在公衆號裏被人批(dui)評(gang)了,大概意思就是:你別老整那ECDH又是橢圓又是素數啥的,你就說這玩意實際項目中怎麼用就完了,我們不想聽那些,那些我們都懂都精通,而且你還太監了,你自己看看是不是太監了,ECDH寫到上一篇明顯還沒完,結果到現在了還沒下文,你自己說是不是太監了,你自己說。

其次是實際上本篇內容實際上和ECDH沒有半毛錢關係,通篇都是DH(少了EC兩個字母),不過在項目中實際應用的業務邏輯寫法、道理都是一樣曬兒的。你現在可以暫時認爲DH就是ECDH的“ 少了兩個字母版本 ”。用DH的最主要原因是啥呢,因爲時間有限,我優先寫了DH的常用語言庫文件,目前可用,ECDH的一根毛都沒有寫,所以只能用DH演示。

最後是再次強調一遍,作爲一篇正經的文章,我需要再次科普一下DH是啥意思。

很多都以爲DH是Daemon Hunter(惡魔獵手)的簡稱,然而並不是。Daemon Hunter是真實名稱叫做伊利丹,是個瞎子同時又是法瑪麗奧(就是老鹿)的兄dei。他暗戀白虎(就是那種真的白虎)泰蘭德,然而泰蘭德卻嫁給了老鹿,事情大概就是這麼一回事。

在我們這裏DH則是Diffie-Hellman的簡稱,二位大爺的照片我以前貼過,現在不得不再貼一遍:

上圖告訴我們頭髮長短與職業無關,douyin上那些自以爲get到程序員梗的短視頻真的是LOWB到一塌糊塗。

在正式開始前之前,我還是要說明一下用DH的初衷是什麼或者說這個東西是來解決什麼問題的。接着上篇的故事(點擊這裏)說:

  • 你老闆說項目非常牛逼,數據要加密,用牛逼的加密算法
  • 你就用RSA非對稱加密開發測試操作猛如虎
  • 然後,一上線:CPU炸了,成績1-5
  • 然後你找老闆審批升級服務器費用,老闆給了你300塊並讓你放心花大膽花
  • 你首先把RSA下線了,然後偷偷換成了AES對稱加密,CPU不炸了
  • 然後三百塊偷偷放到了自己腰包裏
  • 但是AES的對稱密鑰你寫死到客戶端,被逆向就完了;如果通過服務器下發,聽起來更加扯淡
  • 想了想,你拿着三百塊錢組了個局兒,你帶着錢,我帶着陳旭,老趙帶着柱子,再加上大彪,正好六人局
  • 局上我向你透露出一種方案:將AES對稱密鑰通過非對稱方式協商出來。DH這種神奇的算法可以讓你服務器和客戶端在不傳輸該對稱密鑰的情況下就可以通過心有靈犀地方式各自計算出一個對稱密鑰,而且可以一樣,避免了該密鑰在網絡上流通,而且你可以隨意更換,過期時間定爲1分鐘,可謂是狠毒至極!

我們引入DH就是爲了解決上面的問題。然而,DH或ECDH並不能解決中間人攻擊問題,這個要搞明白了。

所以,在正式開始之前,我必須先安利我和東北大嫖客還有巨蛀以及阿尼特寫的DH庫,github鏈接是這個,下面我將利用這些DH庫們進行demo演示。

https://github.com/ti-dh
(明眼人已經看出來我是來騙star的)

目前這個庫提供了純PHP、C實現的PHP擴展、Java版,列個表格吧:

圖片描述

先說下服務端和客戶端進行協商地整體流程,非常非常簡單:

整個協商流程中,只有第二步和第三步會發生數據交互。第二步是API下發p、g、server-num給客戶端;第三步是客戶端向API提交client-num數據;最後一步,對稱加解密用的key就已經計算出來用於生產環境了。

下面我用世界上最好的語言演示一下如何使用這個鬼東西,客戶端我們用什麼演示呢?客戶端也依然使用世界上最好的語言來演示。首先,你們把上面github裏的庫文件集成到你們API裏,我這裏集成完畢後代碼如下:

API demo code:

<?php
class DhController extends BaseController{

  private $dh = null;

  // 將DH庫初始化進來呀...
  public function init() {
    $this->dh = new Dh();
  }

  // 這就是上圖中的第二步:客戶端訪問這個API獲取g p 和 server-num
  public function getdhbasedataAction() {
    $ret = $this->dh->getdhbasedata();
    echo json_encode( $ret );
  }

  // 這就是上圖中的第三步:客戶端通過這個api提交client-num參數
  public function postdhclientdataAction() {
    if ( $this->getRequest()->isPost() ) {
      if ( empty( $_POST['client_number'] ) || !is_numeric( $_POST['client_number'] ) ) {
        exit( json_encode( array(
          'code'    => -1,
          'message' => 'wrong parameters',
        ) ) );
      }
      $ret = $this->dh->postdhclientdata( $_POST );
      echo json_encode( array(
        'key' => $ret,
      ) );
    }
  }

}

Client demo code:

<?php
require __DIR__ . '/vendor/autoload.php';
use \Curl\Curl;
$curl = new Curl();
// 初始化客戶端數據,隨機一個即可~
$client_number = mt_rand( 100000, 999999 );
// 1、第一步,獲取服務器的p、g和server_number
$ret = $curl->get( 'https://xxxx.ooo/dh/getdhbasedata' );
$ret = json_decode( $ret, true );
$p = $ret['p'];
$g = $ret['g'];
$server_number = $ret['server_number'];
// 2、第二步,根據服務器獲取到的數據計算出client-number
$process_client_number = gmp_powm( $g, $client_number, $p );
// 3、第三步,將計算過後的client-number發送給服務器
// 那個demo裏已經有完美的演示了,多看代碼
$ret = $curl->post( 'https://xxxx.ooo/dh/postdhclientdata', array(
  'client_number' => gmp_strval( $process_client_number ),
) );
$ret = json_decode( $ret, true );
// 4、第四步,根據server-number,client-number和p 計算出公共密鑰K
$key = gmp_powm( $server_number, $client_number, $p );
echo PHP_EOL."DH非對稱密鑰產生交換:".PHP_EOL;
echo 'client計算出的public key : '.$key.PHP_EOL;
echo 'server計算出的public key : '.$ret['key'].PHP_EOL.PHP_EOL;

客戶端文件保存client.php,然後php client.php執行一下,結果你們感受一下:

一樣有沒有?!計算出來的都一樣,有沒有?!!

上圖中那麼一坨長的不能整的讓人看了就覺得噁心嘔吐的數字就是API和客戶端分別計算出來的對稱加解密的密鑰了,請注意實際使用過程中,服務器千萬不要把這個數據返回給客戶端,demo裏這麼做就是爲了演示而已,用的時候自己也需要動動腦子的。

然而,事情往往不會說就是這麼簡單就可以了,如果在生產環境使用,還是需要繼續完善一些細節的。

  • 第一個問題就是有些想的比較多的寶貝兒們會不同的兩個客戶端計算出來的key會不會一樣?可能性非常非常非常小
  • 第二個問題就是一般客戶端登陸的用戶都有自己的token或uid之類的,API這裏在與一個客戶端協商出一個key後可以以 “ token:key ” 格式把key存儲到redis中,然後給一個有效時間比如30分鐘;客戶端也將key保存到手機內存中設置一個30分鐘有效期。每次使用key進行加解密前都驗證一下是否過期,如果過期了就重新走一遍前面的協商流程

我發誓,這是關於DH或ECDH的最後一篇文章了,以後我再也不會寫任何與這兩個英文縮寫相關的東西了,我說都是真的,我保證說到做到。

歡迎來公衆號懟我、槓我:

圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章