Golang 实现 Laravel 的 内置加密类Encrypter->encrypt() 以及 decrypt
golang写的与php框架laravel内置安全(加密算法对接)
Golang
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"errors"
"strings"
"github.com/elliotchance/phpserialize"
"io"
"bytes"
"crypto/rand"
"crypto/hmac"
"crypto/sha256"
"fmt"
"os"
)
func main() {
//加密
token, err := encode("sxin0")
if err != nil {
}
println(token)
os.Exit(1)
//解密
str, err := decode("eyJpdiI6IkJLZmJoOTBGa1A0MGRiLy8zemg4c1E9PSIsIm1hYyI6IjAzZDZiZGQ5YWY4NGY2NGZkMTgwMmFjZTFkZWMwNDgzM2I4ZmUyZTUzOTI2OGY5ZjEzNDQ1OGMwMWE2YmYxYzYiLCJ2YWx1ZSI6IlBqQ1llMW81eFlIZUppaEgyQldrdWxpQzNRb3kyaHNCb0hlS1JTTDR0b2s9In0=")
if err != nil {
}
println(str)
os.Exit(2)
}
//加密
func encode(ciphertext string) (string,error) {
//初始化密钥
key:= []byte("1a04c2a6bl6341639118a9bdbbea545a")
//序列化密文
ciphertextNew,err := phpserialize.Marshal(ciphertext,nil)
if err != nil {
return "",err
}
plaintext := []byte(ciphertextNew)
//填充明文至加密要求长度
/* paddingCount := aes.BlockSize - len(plaintext)%aes.BlockSize
if paddingCount != 0 {
plaintext = append(plaintext, bytes.Repeat([]byte{byte(0)}, paddingCount)...)
}*/
plaintext,err = Pad(plaintext,aes.BlockSize)
if err != nil {
return "",err
}
// CBC mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
// assume that the plaintext is already of the correct length.
//检查密文长度是否合法
/*if len(plaintext)%aes.BlockSize != 0 {
return "",err
}*/
block, err := aes.NewCipher(key)
if err != nil {
return "",err
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "",err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(plaintext, plaintext)
//原加密算法
/*type payload struct {
IV string
Value string
Mac string
}
p := payload{}
p.IV = base64.StdEncoding.EncodeToString(iv)
p.Value = base64.StdEncoding.EncodeToString(plaintext)
data, err := json.Marshal(p)*/
//现在加密算法
payload := make(map[string]string)
payload["iv"] = base64.StdEncoding.EncodeToString(iv)
payload["value"] = base64.StdEncoding.EncodeToString(plaintext)
//生成mac
h := hmac.New(sha256.New,[]byte(key))
io.WriteString(h,payload["iv"]+payload["value"])
payload["mac"] = fmt.Sprintf("%x", h.Sum(nil))
//转json
data, err := json.Marshal(payload)
if err != nil {
return "",err
}
ciphertext = base64.StdEncoding.EncodeToString(data)
return ciphertext,nil
}
//解密
func decode(ciphertext string) (string, error) {
//初始化密钥
key:= "1a04c2a6bl6341639118a9bdbbea545a"
decodeBytes, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return "", errors.New("ciphertext value must in base64 format")
}
var payload struct {
IV string
Value string
Mac string
}
err = json.Unmarshal(decodeBytes, &payload)
if err != nil {
return "", errors.New("ciphertext value must be valid")
}
encryptedText, err := base64.StdEncoding.DecodeString(payload.Value)
if err != nil {
return "", errors.New("encrypted text must be valid base64 format")
}
iv, err := base64.StdEncoding.DecodeString(payload.IV)
if err != nil {
return "", errors.New("iv in payload must be valid base64 format")
}
var keyBytes []byte
if strings.HasPrefix(key, "base64:") {
keyBytes, err = base64.StdEncoding.DecodeString(string(key[7:]))
if err != nil {
return "", errors.New("seems like you provide a key in base64 format, but it's not valid")
}
} else {
keyBytes = []byte(key)
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return "", err
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(encryptedText, encryptedText)
var cleartext string
err = phpserialize.Unmarshal(encryptedText, &cleartext)
return cleartext, nil
}
//ase-256-cbc长度填充
func Pad(src []byte, blockSize int) ([]byte, error) {
// 按标准只允许1 - 255个大小的块.
if blockSize < 1 || blockSize > 255 {
return nil, errors.New("pkcs7: block size must be between 1 and 255 inclusive")
}
// 通过设定目标块大小来计算所需填充的长度
// 减去源的溢出
padLen := blockSize - len(src)%blockSize
// 将包含要重复的字节的字节片.
padding := []byte{byte(padLen)}
// 重复那个字节padLen时间
padding = bytes.Repeat(padding, padLen)
// 向src追加填充.
return append(src, padding...), nil
}
PHP代码
$aes = new Encrypter();
echo $aes->encrypt('exampleplaintext')."\n";
echo $aes->decrypt("eyJpdiI6IkJLZmJoOTBGa1A0MGRiLy8zemg4c1E9PSIsIm1hYyI6IjAzZDZiZGQ5YWY4NGY2NGZkMTgwMmFjZTFkZWMwNDgzM2I4ZmUyZTUzOTI2OGY5ZjEzNDQ1OGMwMWE2YmYxYzYiLCJ2YWx1ZSI6IlBqQ1llMW81eFlIZUppaEgyQldrdWxpQzNRb3kyaHNCb0hlS1JTTDR0b2s9In0=");
class Encrypter
{
/**
* The encryption key.
*
* @var string
*/
protected $key;
/**
* The algorithm used for encryption.
*
* @var string
*/
protected $cipher;
/**
* Create a new encrypter instance.
*
* @param string $key
* @param string $cipher
* @return void
*
* @throws \RuntimeException
*/
//1e04f2f6bd6341639118a9bdbbae545b
public function __construct($key = '1a04c2a6bl6341639118a9bdbbea545a', $cipher = 'AES-256-CBC')
{
$key = (string) $key;
if (static::supported($key, $cipher)) {
$this->key = $key;
$this->cipher = $cipher;
} else {
throw new RuntimeException('The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.');
}
}
/**
* Determine if the given key and cipher combination is valid.
*
* @param string $key
* @param string $cipher
* @return bool
*/
public static function supported($key, $cipher)
{
$length = mb_strlen($key, '8bit');
return ($cipher === 'AES-128-CBC' && $length === 16) || ($cipher === 'AES-256-CBC' && $length === 32);
}
/**
* Encrypt the given value.
*
* @param string $value
* @return string
*
* @throws \Illuminate\Contracts\Encryption\EncryptException
*/
public function encrypt($value)
{
$iv = random_bytes(16);
$value = \openssl_encrypt(serialize($value), $this->cipher, $this->key, 0, $iv);
if ($value === false) {
throw new EncryptException('Could not encrypt the data.');
}
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
$json = json_encode(compact('iv', 'value', 'mac'));
if (! is_string($json)) {
throw new EncryptException('Could not encrypt the data.');
}
return base64_encode($json);
}
/**
* Decrypt the given value.
*
* @param string $payload
* @return string
*
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
public function decrypt($payload)
{
$payload = $this->getJsonPayload($payload);
$iv = base64_decode($payload['iv']);
$decrypted = \openssl_decrypt($payload['value'], $this->cipher, $this->key, 0, $iv);
if ($decrypted === false) {
throw new DecryptException('Could not decrypt the data.');
}
return unserialize($decrypted);
}
/**
* Create a MAC for the given value.
*
* @param string $iv
* @param string $value
* @return string
*/
protected function hash($iv, $value)
{
return hash_hmac('sha256', $iv.$value, $this->key);
}
/**
* Get the JSON array from the given payload.
*
* @param string $payload
* @return array
*
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
protected function getJsonPayload($payload)
{
$payload = json_decode(base64_decode($payload), true);
// If the payload is not valid JSON or does not have the proper keys set we will
// assume it is invalid and bail out of the routine since we will not be able
// to decrypt the given value. We'll also check the MAC for this encryption.
if (! $this->validPayload($payload)) {
throw new DecryptException('The payload is invalid.');
}
if (! $this->validMac($payload)) {
throw new DecryptException('The MAC is invalid.');
}
return $payload;
}
/**
* Verify that the encryption payload is valid.
*
* @param mixed $payload
* @return bool
*/
protected function validPayload($payload)
{
return is_array($payload) && isset($payload['iv'], $payload['value'], $payload['mac']);
}
/**
* Determine if the MAC for the given payload is valid.
*
* @param array $payload
* @return bool
*/
protected function validMac(array $payload)
{
$bytes = random_bytes(16);
$calcMac = hash_hmac('sha256', $this->hash($payload['iv'], $payload['value']), $bytes, true);
return hash_equals(hash_hmac('sha256', $payload['mac'], $bytes, true), $calcMac);
}
/**
* Get the encryption key.
*
* @return string
*/
public function getKey()
{
return $this->key;
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: