網絡安全問題很重要,尤其是保證數據安全,遇到很多在寫接口的程序員直接都是明文數據傳輸,在我看來這是很不專業的。本人提倡經過接口的數據都要進行加密解密之后進行使用。
這篇文章主要介紹使用PHP開發接口,數據實現RSA加密解密后使用,實例分析了PHP自定義RSA類實現加密與解密的技巧,非常具有實用價值,需要的朋友可以參考下。
簡單介紹RSA
RSA加密算法是最常用的非對稱加密算法,CFCA在證書服務中離不了它。但是有不少新手對它不太了解。下面僅作簡要介紹。RSA是第一個比較完善的公開密鑰算法,它既能用于加密,也能用于數字簽名。RSA以它的三個發明者Ron Rivest, Adi Shamir, Leonard Adleman的名字首字母命名,這個算法經受住了多年深入的密碼分析,雖然密碼分析者既不能證明也不能否定RSA的安全性,但這恰恰說明該算法有一定的可信性,目前它已經成為最流行的公開密鑰算法。RSA的安全基于大數分解的難度。其公鑰和私鑰是一對大素數(100到200位十進制數或更大)的函數。從一個公鑰和密文恢復出明文的難度,等價于分解兩個大素數之積(這是公認的數學難題)。
下面為具體類、實例:
001 | <?php |
002 | |
003 | /** |
004 | * RSA算法類 |
005 | * 簽名及密文編碼:base64字符串/十六進制字符串/二進制字符串流 |
006 | * 填充方式: PKCS1Padding(加解密)/NOPadding(解密) |
007 | * |
008 | * Notice:Only accepts a single block. Block size is equal to the RSA key size! |
009 | * 如密鑰長度為1024 bit,則加密時數據需小于128字節,加上PKCS1Padding本身的11字節信息,所以明文需小于117字節 |
010 | * |
011 | * @author: ZHIHUA_WEI |
012 | * @version: 1.0.0 |
013 | * @date: 2017/06/30 |
014 | */ |
015 | class RSA |
016 | { |
017 | private $pubKey = null; |
018 | private $priKey = null; |
019 | |
020 | /** |
021 | * 構造函數 |
022 | * |
023 | * @param string 公鑰文件(驗簽和加密時傳入) |
024 | * @param string 私鑰文件(簽名和解密時傳入) |
025 | */ |
026 | public function __construct( $public_key_file = '' , $private_key_file = '' ) |
027 | { |
028 | if ( $public_key_file ) { |
029 | $this ->_getPublicKey( $public_key_file ); |
030 | } |
031 | if ( $private_key_file ) { |
032 | $this ->_getPrivateKey( $private_key_file ); |
033 | } |
034 | } |
035 | |
036 | // 私有方法 |
037 | /** |
038 | * 自定義錯誤處理 |
039 | */ |
040 | private function _error( $msg ) |
041 | { |
042 | die ( 'RSA Error:' . $msg ); //TODO |
043 | } |
044 | |
045 | /** |
046 | * 檢測填充類型 |
047 | * 加密只支持PKCS1_PADDING |
048 | * 解密支持PKCS1_PADDING和NO_PADDING |
049 | * |
050 | * @param int 填充模式 |
051 | * @param string 加密en/解密de |
052 | * @return bool |
053 | */ |
054 | private function _checkPadding( $padding , $type ) |
055 | { |
056 | if ( $type == 'en' ) { |
057 | switch ( $padding ) { |
058 | case OPENSSL_PKCS1_PADDING: |
059 | $ret = true; |
060 | break ; |
061 | default : |
062 | $ret = false; |
063 | } |
064 | } else { |
065 | switch ( $padding ) { |
066 | case OPENSSL_PKCS1_PADDING: |
067 | case OPENSSL_NO_PADDING: |
068 | $ret = true; |
069 | break ; |
070 | default : |
071 | $ret = false; |
072 | } |
073 | } |
074 | return $ret ; |
075 | } |
076 | |
077 | private function _encode( $data , $code ) |
078 | { |
079 | switch ( strtolower ( $code )) { |
080 | case 'base64' : |
081 | $data = base64_encode ( '' . $data ); |
082 | break ; |
083 | case 'hex' : |
084 | $data = bin2hex( $data ); |
085 | break ; |
086 | case 'bin' : |
087 | default : |
088 | } |
089 | return $data ; |
090 | } |
091 | |
092 | private function _decode( $data , $code ) |
093 | { |
094 | switch ( strtolower ( $code )) { |
095 | case 'base64' : |
096 | $data = base64_decode ( $data ); |
097 | break ; |
098 | case 'hex' : |
099 | $data = $this ->_hex2bin( $data ); |
100 | break ; |
101 | case 'bin' : |
102 | default : |
103 | } |
104 | return $data ; |
105 | } |
106 | |
107 | private function _getPublicKey( $file ) |
108 | { |
109 | $key_content = $this ->_readFile( $file ); |
110 | if ( $key_content ) { |
111 | $this ->pubKey = openssl_get_publickey( $key_content ); |
112 | } |
113 | } |
114 | |
115 | private function _getPrivateKey( $file ) |
116 | { |
117 | $key_content = $this ->_readFile( $file ); |
118 | if ( $key_content ) { |
119 | $this ->priKey = openssl_get_privatekey( $key_content ); |
120 | } |
121 | } |
122 | |
123 | private function _readFile( $file ) |
124 | { |
125 | $ret = false; |
126 | if (! file_exists ( $file )) { |
127 | $this ->_error( "The file {$file} is not exists" ); |
128 | } else { |
129 | $ret = file_get_contents ( $file ); |
130 | } |
131 | return $ret ; |
132 | } |
133 | |
134 | private function _hex2bin( $hex = false) |
135 | { |
136 | $ret = $hex !== false && preg_match( '/^[0-9a-fA-F]+$/i' , $hex ) ? pack( "H*" , $hex ) : false; |
137 | return $ret ; |
138 | } |
139 | |
140 | /** |
141 | * 生成簽名 |
142 | * |
143 | * @param string 簽名材料 |
144 | * @param string 簽名編碼(base64/hex/bin) |
145 | * @return 簽名值 |
146 | */ |
147 | public function sign( $data , $code = 'base64' ) |
148 | { |
149 | $ret = false; |
150 | if (openssl_sign( $data , $ret , $this ->priKey)) { |
151 | $ret = $this ->_encode( $ret , $code ); |
152 | } |
153 | return $ret ; |
154 | } |
155 | |
156 | /** |
157 | * 驗證簽名 |
158 | * |
159 | * @param string 簽名材料 |
160 | * @param string 簽名值 |
161 | * @param string 簽名編碼(base64/hex/bin) |
162 | * @return bool |
163 | */ |
164 | public function verify( $data , $sign , $code = 'base64' ) |
165 | { |
166 | $ret = false; |
167 | $sign = $this ->_decode( $sign , $code ); |
168 | if ( $sign !== false) { |
169 | switch (openssl_verify( $data , $sign , $this ->pubKey)) { |
170 | case 1: |
171 | $ret = true; |
172 | break ; |
173 | case 0: |
174 | case -1: |
175 | default : |
176 | $ret = false; |
177 | } |
178 | } |
179 | return $ret ; |
180 | } |
181 | |
182 | /** |
183 | * 加密 |
184 | * |
185 | * @param string 明文 |
186 | * @param string 密文編碼(base64/hex/bin) |
187 | * @param int 填充方式(貌似php有bug,所以目前僅支持OPENSSL_PKCS1_PADDING) |
188 | * @return string 密文 |
189 | */ |
190 | public function encrypt( $data , $code = 'base64' , $padding = OPENSSL_PKCS1_PADDING) |
191 | { |
192 | $ret = false; |
193 | if (! $this ->_checkPadding( $padding , 'en' )) $this ->_error( 'padding error' ); |
194 | if (openssl_public_encrypt( $data , $result , $this ->pubKey, $padding )) { |
195 | $ret = $this ->_encode( $result , $code ); |
196 | } |
197 | return $ret ; |
198 | } |
199 | |
200 | /** |
201 | * 解密 |
202 | * |
203 | * @param string 密文 |
204 | * @param string 密文編碼(base64/hex/bin) |
205 | * @param int 填充方式(OPENSSL_PKCS1_PADDING / OPENSSL_NO_PADDING) |
206 | * @param bool 是否翻轉明文(When passing Microsoft CryptoAPI-generated RSA cyphertext, revert the bytes in the block) |
207 | * @return string 明文 |
208 | */ |
209 | public function decrypt( $data , $code = 'base64' , $padding = OPENSSL_PKCS1_PADDING, $rev = false) |
210 | { |
211 | $ret = false; |
212 | $data = $this ->_decode( $data , $code ); |
213 | if (! $this ->_checkPadding( $padding , 'de' )) $this ->_error( 'padding error' ); |
214 | if ( $data !== false) { |
215 | if (openssl_private_decrypt( $data , $result , $this ->priKey, $padding )) { |
216 | $ret = $rev ? rtrim( strrev ( $result ), "\0" ) : '' . $result ; |
217 | } |
218 | } |
219 | return $ret ; |
220 | } |
221 | } |
此為具體的RSA類
01 | <?php |
02 | /** |
03 | * Author: Wei ZhiHua |
04 | * Date: 2017/6/30 0030 |
05 | * Time: 上午 10:15 |
06 | */ |
07 | header( 'Content-Type:text/html;Charset=utf-8;' ); |
08 | include "RSA.php" ; |
09 | echo '<pre>' ; |
10 | |
11 | $pubfile = 'D:\WWW\test\rsa_public_key.pem' ; |
12 | $prifile = 'D:\WWW\test\rsa_private_key.pem' ; |
13 | $rsa = new RSA( $pubfile , $prifile ); |
14 | $rst = array ( |
15 | 'ret' => 200, |
16 | 'code' => 1, |
17 | 'data' => array (1, 2, 3, 4, 5, 6), |
18 | 'msg' => "success" , |
19 | ); |
20 | $ex = json_encode( $rst ); |
21 | //加密 |
22 | $ret_e = $rsa ->encrypt( $ex ); |
23 | //解密 |
24 | $ret_d = $rsa ->decrypt( $ret_e ); |
25 | echo $ret_e ; |
26 | echo '<pre>' ; |
27 | echo $ret_d ; |
28 | echo '<pre>' ; |
29 | $a = 'test' ; |
30 | //簽名 |
31 | $x = $rsa ->sign( $a ); |
32 | //驗證 |
33 | $y = $rsa ->verify( $a , $x ); |
34 | var_dump( $x , $y ); |
35 | exit ; |