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

技术标签: 加密解密  c语言  go  php  go学习笔记  

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不满足长度又要怎么处理呢

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a_lzq/article/details/108204967

智能推荐

基于modelscope平台搭建百川13b2的对话模型demo_from modelscope import snapshot_download-程序员宅基地

文章浏览阅读482次。基于魔搭平台快速搭建自己的baichuan大模型demo,简单快速一键部署_from modelscope import snapshot_download

索引存储结构和分块(索引)查找_索引存储结构举例-程序员宅基地

文章浏览阅读2.7k次。索引存储结构索引存储结构=数据表+索引表索引表(index blocked table)中的每一项称为索引项,索引项的一般形式:(关键字,地址)举个例子:分块查找的基本思想分块查找又称分块索引查找(index blocked search)是一种介于顺序查找和二分查找之间的查找方法。其基本思想是:(1)将数据表str[0…n-1]均分为b块,前b-1块中记录个数为s=n/b,最后一块即第b块的记录数小于等于s;(2)每一块中的关键字不一定有序,但前一块中的最大关键字必须小于后一块中的最小关键_索引存储结构举例

ICPC训练联盟2021寒假冬令营(5)_2021.01.22_笔记_icpc2021寒假冬令营第五天-程序员宅基地

文章浏览阅读413次。试题链接点我进入代码提交OJ学习笔记-高效排序算法( O(nlogn)时间复杂度 )算法介绍归并排序主要思路• 归并排序,是把待排序的序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序的序列。• 归并排序的算法核心步骤分为两个部分:分解,合并:• 首先,把n 个元素分解为n 个长度为1的有序子表;• 然后,进行两两归并使元素的关键字有序,得到n/2 个长度为2 的有序子表;• 重复上述合并步骤,直到所有元素合并成一个长度为n的有序表 为止。算法图解算法代_icpc2021寒假冬令营第五天

macos安装ffmpeg以及出现问题的解决方案,一次成功-程序员宅基地

文章浏览阅读1.7k次。安装ffmpeg使用brew首先安装brew:/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"出现这个:==> Installation successful!安装成功....No changes to formulae...._installing ffmpeg dependency: imath ==> pouring imath-3.1.4.catalina.bottle.

CSDN 周赛填空题,充满恶意的嘲讽-程序员宅基地

文章浏览阅读731次。老顾最近一直在玩 csdn 周赛,没啥想法,就是想票点小玩意,之前从第四十一期开始,题型进行了扩展,增加了填空、判断、单选。扩展题型的周赛,目前是三期,分别是四十一,四十四,四十五。连续三期,老顾的填空题一分没拿到过,嗯,尽管41期是老顾提交的bug,自己给自己扣的分。

02-区块链入门之windows 安装以太坊 ethereum 客户端 (win7-64)_window.ethereum详解-程序员宅基地

文章浏览阅读990次。本文目录1.下载以太坊2.开始安装 3. 开启以太坊挖矿 以太坊(Ethereum)是一个运行智能合约的去中心化平台(Platform for Smart Contract),平台上的应用按程序设定运行,不存在停机、审查、欺诈、第三方人为干预的可能。以太坊平台由Golang、C++、Python 等多种编程语言实现。前段时间的The DAO事件让以太坊平台损失惨重,以太坊也分裂成ETC和ETH,..._window.ethereum详解

随便推点

java常用排序算法——二分查找(折半查找)概述-程序员宅基地

文章浏览阅读585次,点赞15次,收藏8次。java常用排序算法——二分查找(折半查找)概述

Plugging an Unplugged Pluggable Database-程序员宅基地

文章浏览阅读108次。1.unplugTo unplug a PDB, you first close it and then generate an XML manifest file. The XML file contains information about the names and the full paths of the tablespaces, as we..._pluggable database is not closed

长虹电器旗舰店被“薅”倒闭背后的风险与防控_长虹电器失败风险的原因-程序员宅基地

文章浏览阅读265次。10月10日,某大型电商平台上一家名为“长虹厨房电器旗舰店”的商家公告称,因为某推广机构的恶意欺骗,标价60多元的电热水壶,被不到10元钱买下拍了20万单。由此产生巨大的损失,甚至有破产倒闭风险,恳请消费者退款。有网友认为“薅羊毛”不能薅秃了羊,决定申请退款;也有网友观点认为,消费者并无过错,商家理应为自己的行为负责,营销活动中出现了问题不应该将责任推给消费者,电商平台应该加强没有契约精神的商家加强监管。这并非电商平台上首次因“薅羊毛”造成电商损失的情况。2019年1月,有网友称拼多多存在重大Bug_长虹电器失败风险的原因

计算机组成原理运算器的编程实现_计算机组成原理编写代码完成累加器和寄存器的内容相减-程序员宅基地

文章浏览阅读7.8k次,点赞4次,收藏13次。一、实验目的 1、加深对计算机中运算器的基本概念、运算方法和基本原理的理解。 2、提高学生对计算机编程语言掌握的熟练程度。二、实验内容 运用一种编程语言实现计算器的基本功能。 思考: 1、计算机中数据是如何表示的? 2、计算机中二进制数的加、减、乘、除四则运算如何实现? 3、运算器的基本结构和功能有哪些?三.实验结果分析 计算器运行结果: 1: 计算机中数据是如何表示的?计算机_计算机组成原理编写代码完成累加器和寄存器的内容相减

嵌入式C语言基础知识-程序员宅基地

文章浏览阅读1.5k次,点赞51次,收藏31次。C语言基础知识:输入输出、运算符、算数运算符、赋值运算符、比较运算符、位运算符、类型转换、控制语句 if语句、if...else语句 、三目运算符、else...if 语句、switch语句、跳转关键字、break、continue、goto.

c语言笔记1 输入和输出注意事项,常量 变量 static_c语言输出行列式分析和注意-程序员宅基地

文章浏览阅读795次。格式输入、格式输出、宏常量、静态局部变量,静态全局变量,bool类型变量,_c语言输出行列式分析和注意

推荐文章

热门文章

相关标签