我目前正在将一个加密算法从PHP翻译成Typescript,以便在一个非常特殊的API中使用,该API要求发布的数据使用API密钥和Secret进行加密。下面是提供的示例,说明如何在PHP中正确地加密数据,以便与API一起使用(实现密钥和IV的方式不能更改):
$iv = substr(hash("SHA256", $this->ApiKey, true), 0, 16);
$key = md5($this->ApiSecret);
$output = openssl_encrypt($Data, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
$completedEncryption = $this->base64Url_Encode($output);
return $completedEncryption;
- 在上面的代码中,*
base64Url_Encode
* 函数所做的唯一事情就是将二进制数据转换为有效的Base64URL字符串 *。
现在是我在Typescript中实现的代码:
import { createHash, createCipheriv } from 'node:crypto'
const secretIV = createHash('sha256').update(this.ApiKey).digest().subarray(0, 16)
// Generate key
/*
Because the OpenSSL function in PHP automatically pads the string with /null chars,
do the same inside NodeJS, so that CreateCipherIV can accept it as a 32-byte key,
instead of a 16-byte one.
*/
const md5 = createHash('md5').update(this.ApiSecret).digest()
const key = Buffer.alloc(32)
key.set(md5, 0)
// Create Cipher
const cipher = createCipheriv('aes-256-cbc', key, secretIV)
let encrypted = cipher.update(data, 'utf8', 'binary');
encrypted += cipher.final('binary');
// Return base64URL string
return Buffer.from(encrypted).toString('base64url');
上面的Typescript代码和前面的PHP代码给予相同的输出。我已经研究了原始的OpenSSL代码,我已确保填充算法匹配(pcks5
和pcks7
)并检查每个输入Buffer是否与PHP中的输入具有相同的字节长度。我目前在想,是否是某种二进制畸形导致数据在Javascript中发生变化?
我希望有Maven能帮我解答这个问题。也许我忽略了什么。先谢了。
1条答案
按热度按时间qnakjoqk1#
PHP中的
md5
函数比较愚蠢,它默认为十六进制输出而不是二进制输出:这也是为什么代码不会抱怨密钥(由MD5散列构造而成)太小的原因,它在ASCII或UTF8编码后输入32字节,而不是AES-128使用的16字节。
显然它使用的是小写编码,尽管甚至没有指定。您也可以指定NodeJS的编码,请参阅
digest
方法的文档。它似乎也使用了小写编码,尽管我也无法直接找到编码的确切规范。一旦你完成了你的任务,请尽快删除代码,因为你永远不应该从密钥计算IV;密钥和IV组合应该是唯一的,因此如果密钥被重用,则上述代码不是IND-CPA安全的。
如果你想知道为什么它是如此愚蠢:MD5的输出已经在标准中有了规定,是二进制的,而且从函数中不可能看到它在做什么,你必须查找代码,如果你在做比较,它也会很糟糕;即使你在比较字符串,那么使用大写而不是小写是很容易的(两者都同样有效,大写十六进制实际上更容易为人类阅读,因为我们出于某种原因更多地关注字母的顶部)。
基本上,它采取了最小意外的原则,并把它扔到了窗口之外。输出的编码可以做得最优,NodeJS实现正确地做到了这一点。