GO-使用golang解密使用php的openssl_encrypt加密的结果以及key的长度不满足要求时的处理方法(AES-128-CBC/AES-256-CBC)

GO-使用golang解密使用php的openssl_encrypt加密的结果以及key的长度不满足要求时的处理方法


注:加密类型为AES-128-ECB的参考– https://blog.csdn.net/a_lzq/article/details/108392654
不同语言之间的通讯尤其涉及到标准加密的情况的时候往往有很多不标准的事情闹得人焦头烂额的.今天就在处理php和go的加密通讯的时候遇到了问题.
首先来看下正常的情况下的通讯
php加密部分

<?php
$iv = "1234567890abcdef";
$key = "abcdef1234567890";
//使用OPENSSL_RAW_DATA,多一次转换,方便说明白通讯过程
echo base64_encode(openssl_encrypt("hello world", "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv));

结果

CtqXZg7SH5ACIK7gWwOu4w==

go解密部分

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
)
func main() {
originData , _ := base64.StdEncoding.DecodeString( "CtqXZg7SH5ACIK7gWwOu4w==" )
	iv := []byte("1234567890abcdef")
	keyByteString := "abcdef1234567890"
	cipherBlock, err := aes.NewCipher([]byte(keyByteString))
	if err != nil{
		fmt.Println(err)
	}
	cipher.NewCBCDecrypter(cipherBlock, iv).CryptBlocks(originData, originData)
	fmt.Println(string(PKCS5UnPadding(originData)))
}

//这个方法是直接找的网上的实现
func PKCS5UnPadding(src []byte) []byte {
	length := len(src)
	unpadding := int(src[length-1])
	if length - unpadding < 0 {
		return []byte("")
	}
	return src[:(length - unpadding)]
}

解密结果

hello world

Process finished with exit code 0

说明在AES-128-CBC的时候keyByteString是16个字符这个是固定的

再看下AES-256-CBC的时候

<?php
$iv = "1234567890abcdef";
$key = "abcdef12345678901234567890abcdef";

echo base64_encode(openssl_encrypt("hello world", "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv));

结果

RxpbfHR/FnUo5Mh9NRtudQ==
package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
)
func main() {
originData , _ := base64.StdEncoding.DecodeString( "RxpbfHR/FnUo5Mh9NRtudQ==" )
	iv := []byte("1234567890abcdef")
	keyByteString := "abcdef12345678901234567890abcdef"
	cipherBlock, err := aes.NewCipher([]byte(keyByteString))
	if err != nil{
		fmt.Println(err)
	}
	cipher.NewCBCDecrypter(cipherBlock, iv).CryptBlocks(originData, originData)
	fmt.Println(string(PKCS5UnPadding(originData)))
}

//这个方法是直接找的网上的实现
func PKCS5UnPadding(src []byte) []byte {
	length := len(src)
	unpadding := int(src[length-1])
	if length - unpadding < 0 {
		return []byte("")
	}
	return src[:(length - unpadding)]
}

结果

hello world

Process finished with exit code 0

其中iv是固定16位,而keyByteString则变为了32位

以上这几种情况都比较简单,而比较坑的是啥呢,
1.php在key的长度不满足要求的时候也可以加密成功!
2.go则必须要求keyByteString的长度满足要求!!!
所以这几涉及到了key在php中到底是怎处理的
先看下php的源码

PHP_FUNCTION(openssl_decrypt)
{
	//省略无关代码
	//注意这里这里有对参数的初始化处理,那么我们就可以猜测这里有对key的处理
	if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
				&password, &password_len, &free_password,
				&iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
			php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
				data, data_len, aad, aad_len, 0) == FAILURE) {
		RETVAL_FALSE;
	}
	//省略好多
}
//再来看php_openssl_cipher_init方法
static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
		EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
		char **ppassword, size_t *ppassword_len, zend_bool *free_password,
		char **piv, size_t *piv_len, zend_bool *free_iv,
		char *tag, int tag_len, zend_long options, int enc)  /* {{{ */
{
	//又省略好多
	/* check and set key */
	password_len = (int) *ppassword_len;
	key_len = EVP_CIPHER_key_length(cipher_type);
	//就是这里了这里PHP在key的长度不满足要求的长度时会自动重新填充一个新的key出来!而go中不支持这种操作!!!这就比较坑了,而解决的办法就是通过go实现key的填充
	if (key_len > password_len) {
		if ((OPENSSL_DONT_ZERO_PAD_KEY & options) && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
			php_openssl_store_errors();
			php_error_docref(NULL, E_WARNING, "Key length cannot be set for the cipher method");
			return FAILURE;
		}
		key = emalloc(key_len);
		memset(key, 0, key_len);
		memcpy(key, *ppassword, password_len);
		*ppassword = (char *) key;
		*ppassword_len = key_len;
		*free_password = 1;
	} 
	//省略好多
}

那么如何解决这个问题呢
直接看看代码吧

<?php
$iv = "1234567890abcdef";
$key = "abc";

echo base64_encode(openssl_encrypt("hello world", "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv));

结果

7s3/y57WmZt+f1Pz5KzXlg==

go 的解密处理方法

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
)
func main() {
	originData , _ := base64.StdEncoding.DecodeString( "7s3/y57WmZt+f1Pz5KzXlg==" )
	iv := []byte("1234567890abcdef")

	//key为abc
	key := "abc"
	//初始化一个空的字节数组,这里的16为AES-128-CBC的要求的key的长度
	keyByte := [16]byte{}
	//转换key为字节数组
	keyByteTemp := []byte(key)
	//依次赋值,这里的3为key的len
	for i := 0; i < 3; i++ {
		keyByte[i] = keyByteTemp[i]
	}
	fmt.Println(keyByteTemp,keyByte)
	//转换依次得到的keyByteString
	keyByteString := string(keyByte[:])

	//解密
	cipherBlock, err := aes.NewCipher([]byte(keyByteString))
	if err != nil{
		fmt.Println(err)
	}
	cipher.NewCBCDecrypter(cipherBlock, iv).CryptBlocks(originData, originData)
	fmt.Println(string(PKCS5UnPadding(originData)))
}

func PKCS5UnPadding(src []byte) []byte {
	length := len(src)
	unpadding := int(src[length-1])
	if length - unpadding < 0 {
		return []byte("")
	}
	return src[:(length - unpadding)]
}

结果

[97 98 99] [97 98 99 0 0 0 0 0 0 0 0 0 0 0 0 0]
hello world

Process finished with exit code 0

好了这里是针对AES-128-CBC的在key的长度不满足的情况做的处理
留给读者思考的问题
1.AES-256-CBC的key不满足长度的情况怎处理
2.在AES-128-CBC的情况iv不满足长度又要怎么处理呢

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值