sm4KeySchedule($key); $bytes = $this->pad($data, $this->_block_size); $chunks = array_chunk($bytes, $this->_block_size); $ciphertext = ""; foreach ($chunks as $chunk) { $ciphertext .= $this->sm4Encrypt($chunk); } return base64_encode($ciphertext); } /** * sm4解密 * @param $key * @param $data * @return bool|string */ public function decrypt($key, $data) { $data = base64_decode($data); if (strlen($data) < 0 || strlen($data) % $this->_block_size != 0) { return false; } $this->sm4KeySchedule($key); $bytes = unpack("C*", $data); $chunks = array_chunk($bytes, $this->_block_size); $plaintext = ""; foreach ($chunks as $chunk) { $plaintext .= substr($this->sm4Decrypt($chunk), 0, 16); } $plaintext = $this->unPad($plaintext); return $plaintext; } private function sm4Decrypt($cipherText) { $x = []; for ($j=0; $j<4; $j++) { $x[$j]=($cipherText[$j*4]<<24) |($cipherText[$j*4+1]<<16)| ($cipherText[$j*4+2]<<8)|($cipherText[$j*4+3]); } for ($i=0; $i<32; $i++) { $tmp = $x[$i+1]^$x[$i+2]^$x[$i+3]^$this->_rk[31-$i]; $buf= (self::SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 |(self::SM4_SBOX[($tmp >> 16) & 0xFF]) << 16 |(self::SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 |(self::SM4_SBOX[$tmp & 0xFF]); $x[$i+4]=$x[$i]^($buf^$this->sm4Rotl32(($buf), 2)^ $this->sm4Rotl32(($buf), 10) ^ $this->sm4Rotl32(($buf), 18)^ $this->sm4Rotl32(($buf), 24)); } $plainText = []; for ($k=0; $k<4; $k++) { $plainText[4*$k]=($x[35-$k]>> 24)& 0xFF; $plainText[4*$k+1]=($x[35-$k]>> 16)& 0xFF; $plainText[4*$k+2]=($x[35-$k]>> 8)& 0xFF; $plainText[4*$k+3]=($x[35-$k])& 0xFF; } return $this->bytesToString($plainText); } private function sm4Encrypt($plainText) { $x = []; for ($j=0; $j<4; $j++) { $x[$j]=($plainText[$j*4]<<24) |($plainText[$j*4+1]<<16)| ($plainText[$j*4+2]<<8)|($plainText[$j*4+3]); } for ($i=0; $i<32; $i++) { $tmp = $x[$i+1]^$x[$i+2]^$x[$i+3]^$this->_rk[$i]; $buf= (self::SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 |(self::SM4_SBOX[($tmp >> 16) & 0xFF]) << 16 |(self::SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 |(self::SM4_SBOX[$tmp & 0xFF]); $x[$i+4]=$x[$i]^($buf^$this->sm4Rotl32(($buf), 2)^ $this->sm4Rotl32(($buf), 10) ^ $this->sm4Rotl32(($buf), 18)^ $this->sm4Rotl32(($buf), 24)); } $cipherText = []; for ($k=0; $k<4; $k++) { $cipherText[4*$k]=($x[35-$k]>> 24)& 0xFF; $cipherText[4*$k+1]=($x[35-$k]>> 16)& 0xFF; $cipherText[4*$k+2]=($x[35-$k]>> 8)& 0xFF; $cipherText[4*$k+3]=($x[35-$k])& 0xFF; } return $this->bytesToString($cipherText); } private function stringToBytes($string) { return unpack('C*', $string); } private function bytesToString($bytes) { return vsprintf(str_repeat('%c', count($bytes)), $bytes); } private function pad($data) { $bytes = $this->stringToBytes($data); $rem = $this->_block_size - count($bytes) % $this->_block_size; for ($i = 0; $i < $rem; $i++) { array_push($bytes, $rem); } return $bytes; } private function unPad($data) { $bytes = $this->stringToBytes($data); $rem = $bytes[count($bytes)]; $bytes = array_slice($bytes, 0, count($bytes) - $rem); return $this->bytesToString($bytes); } private function sm4Rotl32($buf, $n) { return (($buf << $n) & 0xffffffff) | ($buf >> (32-$n)); } private function sm4KeySchedule($key) { $this->_rk = []; $key = array_values(unpack("C*", $key)); $k = []; for ($i=0; $i<4; $i++) { $k[$i] = self::SM4_FK[$i]^(($key[4*$i]<<24) | ($key[4*$i+1]<<16) |($key[4*$i+2]<<8) | ($key[4*$i+3])); } for ($j=0; $j<32; $j++) { $tmp = $k[$j+1]^$k[$j+2]^$k[$j+3]^ self::SM4_CK[$j]; $buf = (self::SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 |(self::SM4_SBOX[($tmp >> 16) & 0xFF]) << 16 |(self::SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 |(self::SM4_SBOX[$tmp & 0xFF]); $k[$j+4]=$k[$j]^(($buf)^($this->sm4Rotl32(($buf), 13))^($this->sm4Rotl32(($buf), 23))); $this->_rk[$j]=$k[$j+4]; } } }