[轉]OpenSSL小結

ref: http://www.cnblogs.com/phpinfo/archive/2013/08/09/3246376.htmlphp

公司一個項目要進行交易數據傳輸,由於這個項目銀行那邊也是剛剛開始啓動,全部的支持只有一個傳輸字段的說明文檔,好吧,總的有人作事不是嘛,因而接口開發正式展開,第一步的難點就是加密解密,我選擇使用OpenSSL.html

OpenSSL初接觸的人恐怕最難的在於先理解各類概念java

  公鑰/私鑰/簽名/驗證簽名/加密/解密/非對稱加密linux

  咱們通常的加密是用一個密碼加密文件,而後解密也用一樣的密碼.這很好理解,這個是對稱加密.而有些加密時,加密用的一個密碼,而解密用另一組密碼,這個叫非對稱加密,意思就是加密解密的密碼不同.初次接觸的人恐怕不管如何都理解不了.其實這是數學上的一個素數積求因子的原理的應用,若是你必定要搞懂,百度有大把大把的資料能夠看,其結果就是用這一組密鑰中的一個來加密數據,能夠用另外一個解開.是的沒錯,公鑰和私鑰均可以用來加密數據,相反用另外一個解開,公鑰加密數據,而後私鑰解密的狀況被稱爲加密解密,私鑰加密數據,公鑰解密通常被稱爲簽名和驗證簽名.算法

  由於公鑰加密的數據只有它相對應的私鑰能夠解開,因此你能夠把公鑰給人和人,讓他加密他想要傳送給你的數據,這個數據只有到了有私鑰的你這裏,才能夠解開成有用的數據,其餘人就是獲得了,也看懂內容.同理,若是你用你的私鑰對數據進行簽名,那這個數據就只有配對的公鑰能夠解開,有這個私鑰的只有你,因此若是配對的公鑰解開了數據,就說明這數據是你發的,相反,則不是.這個被稱爲簽名.apache

  實際應用中,通常都是和對方交換公鑰,而後你要發給對方的數據,用他的公鑰加密,他獲得後用他的私鑰解密,他要發給你的數據,用你的公鑰加密,你獲得後用你的私鑰解密,這樣最大程度保證了安全性.windows

  RSA/DSA/SHA/MD5安全

  非對稱加密的算法有不少,比較著名的有RSA/DSA ,不一樣的是RSA能夠用於加/解密,也能夠用於簽名驗籤,DSA則只能用於簽名.至於SHA則是一種和md5相同的算法,它不是用於加密解密或者簽名的,它被稱爲摘要算法.就是經過一種算法,依據數據內容生成一種固定長度的摘要,這串摘要值與原數據存在對應關係,就是原數據會生成這個摘要,可是,這個摘要是不能還原成原數據的,嗯....,正常狀況下是這樣的,這個算法起的做用就是,若是你把原數據修改一點點,那麼生成的摘要都會不一樣,傳輸過程當中把原數據給你再給你一個摘要,你把獲得的原數據一樣作一次摘要算法,與給你的摘要相比較就能夠知道這個數據有沒有在傳輸過程當中被修改了.this

  實際應用過程當中,由於須要加密的數據可能會很大,進行加密費時費力,因此通常都會把原數據先進行摘要,而後對這個摘要值進行加密,將原數據的明文和加密後的摘要值一塊兒傳給你.這樣你解開加密後的摘要值,再和你獲得的數據進行的摘要值對應一下就能夠知道數據有沒有被修改了,並且,由於私鑰只有你有,只有你能解密摘要值,因此別人就算把原數據作了修改,而後生成一個假的摘要給你也是不行的,你這邊用密鑰也根本解不開.編碼

   CA/PEM/DER/X509/PKCS

  通常的公鑰不會用明文傳輸給別人的,正常狀況下都會生成一個文件,這個文件就是公鑰文件,而後這個文件能夠交給其餘人用於加密,可是傳輸過程當中若是有人惡意破壞,將你的公鑰換成了他的公鑰,而後獲得公鑰的一方加密數據,不是他就能夠用他本身的密鑰解密看到數據了嗎,爲了解決這個問題,須要一個公證方來作這個事,任何人均可以找它來確認公鑰是誰發的.這就是CA,CA確認公鑰的原理也很簡單,它將它本身的公鑰發佈給全部人,而後一個想要發佈本身公鑰的人能夠將本身的公鑰和一些身份信息發給CA,CA用本身的密鑰進行加密,這裏也能夠稱爲簽名.而後這個包含了你的公鑰和你的信息的文件就能夠稱爲證書文件了.這樣一來全部獲得一些公鑰文件的人,經過CA的公鑰解密了文件,若是正常解密那麼機密后里面的信息必定是真的,由於加密方只多是CA,其餘人沒它的密鑰啊.這樣你解開公鑰文件,看看裏面的信息就知道這個是否是那個你須要用來加密的公鑰了.

  實際應用中,通常人都不會找CA去簽名,由於那是收錢的,因此能夠本身作一個自簽名的證書文件,就是本身生成一對密鑰,而後再用本身生成的另一對密鑰對這對密鑰進行簽名,這個只用於真正須要簽名證書的人,普通的加密解密數據,直接用公鑰和私鑰來作就能夠了.

  密鑰文件的格式用OpenSSL生成的就只有PEM和DER兩種格式,PEM的是將密鑰用base64編碼表示出來的,直接打開你能看到一串的英文字母,DER格式是二進制的密鑰文件,直接打開,你能夠看到........你什麼也看不懂!.X509是通用的證書文件格式定義.pkcs的一系列標準是指定的存放密鑰的文件標準,你只要知道PEM DER X509 PKCS這幾種格式是能夠互相轉化的.

OpenSSL使用

  PHP的OpenSSL模塊要加載上很容易,百度一下,按着作就能夠,可是這裏面其實有不少問題,首先一點就是windows環境的問題,OpenSSL開發是以linux環境爲基礎的,如今開發者的windows下lamp集成環境會出現不少錯誤,若是你去OpenSSL官網能夠看到它提供了專門的windows環境OpenSSL安裝包.遺憾的是,沒有提供給你PHP調用的解決辦法,因此轉了一圈咱們又回到PHP自帶的php_openssl.dll下了,你會發如今生成證書,提取公鑰等等地方都會出現錯誤.個人解決辦法是,在Apache的bin目錄下用命令行來生成各類證書密鑰,在PHP中只須要提取密鑰作驗證工做或者加密工做就能夠了,密鑰你只生成一次嘛,那個項目也不會須要你不停的變密鑰對是吧.可是你要注意的是即便在Apache下使用命令行,你也須要一個特殊的openssl.cnf,這個能夠用於windows環境,你能夠百度一下,作了這些工做之後,你能夠跟着我來生成各類密鑰了,假設你在apache/bin目錄下,所須要的exr,cnf,dll文件都拷貝到這個目錄下了,而後你打開dos命令行,進入這個目錄,作如下工做:

  

生成rsa密鑰
openssl genrsa -des3 -out prikey.pem
去除掉密鑰文件保護密碼
openssl rsa -in prikey.pem -out prikey.pem
分離出公鑰
openssl rsa -in prikey.pem -pubout -out pubkey.pem(獲取證書中的公鑰 openssl req -in myreq.pem  -out -pubkey.pem)
對文件進行簽名
open rsautl -sign -inkey prikey.pem -in a.txt -out sig.dat
驗證簽名
openssl rsautl -verify -inkey prikey.pem -in sig.dat
用公鑰對文件加密
openssl rsautl -encrypt -pubin -inkey pubkey.pem -in a.text -out b.text
用私鑰解密
openssl rsautl -decrypt -inkey prikey.pem -in b.text
用證書中的公鑰加密 
opensll rsautl -encrypt -certin -inkey cert1.pem -in a.txt

或者


生成一個沒有加密的ca私鑰
openssl genrsa -out ca.key.pem 1024
生成ca對應的csr文件
openssl req -new -key ca.key.pem -out ca.csr
自簽名
openssl x509 -in ca.csr -out ca.cer -req -signkey ca.key.pem -days 7300 -extensions v3_ca
生成DER格式的私鑰
openssl pkcs8 -topk8 -inform PEM -outform DER -in ca.key.pem -out ca.private.der -nocrypt
讀取證書的內容,顯示在屏幕上
openssl x509 -in server.cer -noout -subject  -nameopt RFC2253
將der格式的證書轉成pem格式
openssl  x509  -inform PEM  -outform DER -in server.der -out server.pem

 若是那個命令提示你須要config文件,你在命令行的最後添加-config C:\wamp\bin\apache\apache2.2.8\conf\openssl.cnf,路徑是你openssl.cnf的路徑,根據你的cnf在哪裏放設置.

 

PHP中OpenSSL使用

提取公鑰私鑰

function get_pubkey($cert_file){
		$fp = fopen($cert_file, "r");
		$cert = fread($fp, 8192);
		fclose($fp);
		$pubkey = openssl_get_publickey($cert);
		$pubkeyid=$pubkey;
		openssl_free_key($pubkey);
		return $pubkeyid;
	}
	
function get_privkey($pri_file){
		$fp = fopen($pri_file, "rb");
		$priv_key = fread($fp, 8192);
		fclose($fp);
		$prikey = openssl_get_privatekey($priv_key,$password);
		$prikeyid=$prikey;
		openssl_free_key($prikey);
		return $prikeyid;
}

 簽名,驗證簽名

function sign_data($data,$priv_key){
		openssl_sign($data, $signature, $priv_key);
		$signature=base64_encode($signature);
		return $signature;
}
function verify_sign($data,$signature,$pubkeyid){
		$signature=base64_decode($signature);
		$ok = openssl_verify($data, $signature, $pubkeyid);
		if($ok==1){
			return $ok;
		}else{
			$sha1 = sha1($data, true);
			$sign = base64_decode($signature);
			$deco = '';
			openssl_public_decrypt($sign, $deco, $pubkeyid);
			if($sha1==$deco){
				$ok=1;
			}else{
				$ok=2;
			}
			return $ok;
		}
		
}

 加密,解密代碼可能大家運行不了,有些參數和方法大家沒有,發出來你們參考下,原理同樣的,改改就能用

  function encrypt_data_public( $data_to_encrypt )
    {
      $this->clear_error();


      $this->string_to_encrypt = $data_to_encrypt;


      if (! file_exists($this->public_key_path))
      {
        $this->set_error ( PUBLIC_KEY_ERROR, loc_encrypt_invalid_public_key_path );
        return PUBLIC_KEY_ERROR;
      }

      $fp = fopen ( $this->public_key_path, "r" );
      $public_key_tmp = fread ( $fp, 8192 );
      fclose( $fp );


      $public_key = openssl_get_publickey($public_key_tmp);


      if (!$public_key)
      {
        $this->set_error(PUBLIC_KEY_ERROR, loc_encrypt_openssl_get_public_error);
        openssl_free_key( $public_key );
        return PUBLIC_KEY_ERROR;
      }
      

      openssl_public_encrypt( $this->string_to_encrypt, $encrypted_data_tmp, $public_key );


      if( empty( $encrypted_data_tmp))
      {
        $this->set_error( ENCRYPTION_ERROR, loc_encrypt_empty_return );
        openssl_free_key( $public_key );
        return ENCRYPTION_ERROR;
      }

      $ret = $this->verify_encryption( $encrypted_data_tmp );
      if( $ret != TA_SUCCESS )
      {

        openssl_free_key($public_key);
        return $ret;
      }
      
      $this->encrypted_data = $encrypted_data_tmp;
      openssl_free_key($public_key);
      return TA_SUCCESS;
    }






  
    function decrypt_data_private( $encrypted_data )
    {
      $this->clear_error();


      if(! file_exists($this->private_key_path))
      {
        $this->set_error ( PRIVATE_KEY_ERROR, loc_encrypt_invalid_private_key_path );
        return PRIVATE_KEY_ERROR;
      }

      $fp=fopen ($this->private_key_path,"r");
      $private_key_tmp = fread( $fp, 8192 );
      fclose($fp);
      

      if( $this->passphrase == "" )
      {
        $private_key = openssl_get_privatekey( $private_key_tmp );
      }else{
        $private_key = openssl_get_privatekey( $private_key_tmp, $this->passphrase );
      }


      if (!$private_key)
      {
        $this->set_error(PRIVATE_KEY_ERROR, loc_encrypt_openssl_get_private_error);
        return PRIVATE_KEY_ERROR;
      }
      
      $ret = openssl_private_decrypt( $encrypted_data, $decrypted, $private_key );


      if (!$ret)
      {
        $this->set_error( DECRYPTION_ERROR, loc_encrypt_decryption_private_error );
        openssl_free_key($private_key);
        return DECRYPTION_ERROR;
      }
      $this->decrypted_data = $decrypted;
      openssl_free_key($private_key);
      return TA_SUCCESS;

    }

 

與java對接的問題補充:

  銀聯的接口是java的,他們的java加密機直接生成的是16進制密鑰公鑰字符串,這個公鑰openssl是使用不了的,.網上流傳的將16進制的公鑰轉化爲openssl可用的php方代碼,我能夠確定的告訴各位,都是不成功的,還有不少是java的如何處理和生成openssl兼容的代碼,若是java能夠生成pkcs#8格式的公鑰文件,這個是openssl兼容的,遺憾的是,咱們無法要求銀聯方作什麼,因此只有本身想辦法,個人解決辦法是用php調用銀聯的加密解密模塊,其餘數據處理由php接口作,只是在加密解密的地方,調用JAVA的相關模塊來處理.這個也很麻煩,另外開一篇來講明.

相關文章
相關標籤/搜索