禁用加密填充
Disable crypto padding
如果我使用此代码:
let enc = new TextEncoder();
let data = enc.encode('January February');
let algorithm = {
name: 'AES-CBC', iv: enc.encode('0123456789ABCDEF')
};
crypto.subtle.importKey(
'raw', enc.encode('GHIJKLMNOPQRSTUV'), 'AES-CBC', true, ['encrypt']
).then(
key => crypto.subtle.encrypt(algorithm, key, data)
).then(
ct => console.log(btoa(String.fromCharCode(...new Uint8Array(ct))))
);
我得到这个结果:
q6BAetimbeLcdlSC7GoBbtrh/HM4xs3t1+BzEYxdEIk=
如果我使用这个 PHP 代码,我会得到相同的结果:
echo openssl_encrypt(
'January February', 'aes-128-cbc', 'GHIJKLMNOPQRSTUV', iv: '0123456789ABCDEF'
);
但是 PHP 可以选择禁用填充:
echo openssl_encrypt(
'January February',
'aes-128-cbc',
'GHIJKLMNOPQRSTUV',
OPENSSL_ZERO_PADDING,
'0123456789ABCDEF'
);
结果:
q6BAetimbeLcdlSC7GoBbg==
可以使用 JavaScript 禁用填充吗?如果没有,什么是获得
相同的较短输出?
WebCrypto 在 AES-CBC 上下文中使用 PKCS7 padding by default (), which as far as I know cannot be disabled (s. also the documentation of SubtleCrypto.encrypt()
)。
仅当明文是块大小的整数倍(AES 为 16 字节)时,才可以使用不带填充的 AES-CBC 加密,如您的示例所示。在这种情况下,PKCS7 附加了一个带有填充字节(全部为 0x10)的完整块,即在密文中只需要删除最后一个块:
//
// Your code (implicit base64 encoding)
//
$enc = openssl_encrypt('January February', 'aes-128-cbc', 'GHIJKLMNOPQRSTUV', OPENSSL_ZERO_PADDING, '0123456789ABCDEF');
print($enc . PHP_EOL);
//
// Remove last block (explicit Base64 encoding required, since the last block of the ACTUAL ciphertext must be removed)
//
$enc = base64_encode(substr(openssl_encrypt('January February', 'aes-128-cbc', 'GHIJKLMNOPQRSTUV', OPENSSL_RAW_DATA, '0123456789ABCDEF'), 0, -16));
print($enc . PHP_EOL);
和
let enc = new TextEncoder();
let data = enc.encode('January February');
let algorithm = {
name: 'AES-CBC', iv: enc.encode('0123456789ABCDEF')
};
crypto.subtle.importKey(
'raw', enc.encode('GHIJKLMNOPQRSTUV'), 'AES-CBC', true, ['encrypt']
).then(
key => crypto.subtle.encrypt(algorithm, key, data)
).then(
ct => console.log(btoa(String.fromCharCode(...new Uint8Array(ct).slice(0, -16)))) // reomve last block
);
两个代码片段都提供 q6BAetimbeLcdlSC7GoBbg==
作为输出。
需要注意的是,解密的开销更大,因为必须添加加密的填充字节(如果解密后没有有效的PKCS7填充,DOMException
被抛出)。
当然有 JavaScript 库比低级 WebCrypto API 更舒适,并且还支持不同的填充以及禁用填充,例如CryptoJS:
var data = 'January February';
var key = CryptoJS.enc.Utf8.parse('GHIJKLMNOPQRSTUV');
var iv = CryptoJS.enc.Utf8.parse('0123456789ABCDEF');
var encrypted = CryptoJS.AES.encrypt(
data,
key,
{
iv: iv,
padding: CryptoJS.pad.NoPadding
}
);
console.log(encrypted.toString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
其他答案很好,但作为替代方案,您可以使用内置节点
crypto
API:
let crypto = require('crypto');
let [pt, key, iv] = ['January February', 'GHIJKLMNOPQRSTUV', '0123456789ABCDEF'];
// default is padding, omit last method if needed
let cipher = crypto.createCipheriv('aes-128-cbc', key, iv).setAutoPadding(false);
let ct = cipher.update(pt, 'utf8', 'base64') + cipher.final('base64');
console.log(ct);
这里是一个使用 Deno crypto
库的例子:
import { Aes } from 'http://deno.land/x/crypto/aes.ts';
import { Cbc, Padding } from 'http://deno.land/x/crypto/block-modes.ts';
let te = new TextEncoder;
let [pt, key, iv] = [
te.encode('January February'),
te.encode('GHIJKLMNOPQRSTUV'),
te.encode('0123456789ABCDEF')
];
// default is no padding, omit last argument if needed
let cipher = new Cbc(Aes, key, iv, Padding.PKCS7);
let ct = btoa(String.fromCharCode(...cipher.encrypt(pt)));
console.log(ct);
如果我使用此代码:
let enc = new TextEncoder();
let data = enc.encode('January February');
let algorithm = {
name: 'AES-CBC', iv: enc.encode('0123456789ABCDEF')
};
crypto.subtle.importKey(
'raw', enc.encode('GHIJKLMNOPQRSTUV'), 'AES-CBC', true, ['encrypt']
).then(
key => crypto.subtle.encrypt(algorithm, key, data)
).then(
ct => console.log(btoa(String.fromCharCode(...new Uint8Array(ct))))
);
我得到这个结果:
q6BAetimbeLcdlSC7GoBbtrh/HM4xs3t1+BzEYxdEIk=
如果我使用这个 PHP 代码,我会得到相同的结果:
echo openssl_encrypt(
'January February', 'aes-128-cbc', 'GHIJKLMNOPQRSTUV', iv: '0123456789ABCDEF'
);
但是 PHP 可以选择禁用填充:
echo openssl_encrypt(
'January February',
'aes-128-cbc',
'GHIJKLMNOPQRSTUV',
OPENSSL_ZERO_PADDING,
'0123456789ABCDEF'
);
结果:
q6BAetimbeLcdlSC7GoBbg==
可以使用 JavaScript 禁用填充吗?如果没有,什么是获得 相同的较短输出?
WebCrypto 在 AES-CBC 上下文中使用 PKCS7 padding by default (SubtleCrypto.encrypt()
)。
仅当明文是块大小的整数倍(AES 为 16 字节)时,才可以使用不带填充的 AES-CBC 加密,如您的示例所示。在这种情况下,PKCS7 附加了一个带有填充字节(全部为 0x10)的完整块,即在密文中只需要删除最后一个块:
//
// Your code (implicit base64 encoding)
//
$enc = openssl_encrypt('January February', 'aes-128-cbc', 'GHIJKLMNOPQRSTUV', OPENSSL_ZERO_PADDING, '0123456789ABCDEF');
print($enc . PHP_EOL);
//
// Remove last block (explicit Base64 encoding required, since the last block of the ACTUAL ciphertext must be removed)
//
$enc = base64_encode(substr(openssl_encrypt('January February', 'aes-128-cbc', 'GHIJKLMNOPQRSTUV', OPENSSL_RAW_DATA, '0123456789ABCDEF'), 0, -16));
print($enc . PHP_EOL);
和
let enc = new TextEncoder();
let data = enc.encode('January February');
let algorithm = {
name: 'AES-CBC', iv: enc.encode('0123456789ABCDEF')
};
crypto.subtle.importKey(
'raw', enc.encode('GHIJKLMNOPQRSTUV'), 'AES-CBC', true, ['encrypt']
).then(
key => crypto.subtle.encrypt(algorithm, key, data)
).then(
ct => console.log(btoa(String.fromCharCode(...new Uint8Array(ct).slice(0, -16)))) // reomve last block
);
两个代码片段都提供 q6BAetimbeLcdlSC7GoBbg==
作为输出。
需要注意的是,解密的开销更大,因为必须添加加密的填充字节(如果解密后没有有效的PKCS7填充,DOMException
被抛出)。
当然有 JavaScript 库比低级 WebCrypto API 更舒适,并且还支持不同的填充以及禁用填充,例如CryptoJS:
var data = 'January February';
var key = CryptoJS.enc.Utf8.parse('GHIJKLMNOPQRSTUV');
var iv = CryptoJS.enc.Utf8.parse('0123456789ABCDEF');
var encrypted = CryptoJS.AES.encrypt(
data,
key,
{
iv: iv,
padding: CryptoJS.pad.NoPadding
}
);
console.log(encrypted.toString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
其他答案很好,但作为替代方案,您可以使用内置节点
crypto
API:
let crypto = require('crypto');
let [pt, key, iv] = ['January February', 'GHIJKLMNOPQRSTUV', '0123456789ABCDEF'];
// default is padding, omit last method if needed
let cipher = crypto.createCipheriv('aes-128-cbc', key, iv).setAutoPadding(false);
let ct = cipher.update(pt, 'utf8', 'base64') + cipher.final('base64');
console.log(ct);
这里是一个使用 Deno crypto
库的例子:
import { Aes } from 'http://deno.land/x/crypto/aes.ts';
import { Cbc, Padding } from 'http://deno.land/x/crypto/block-modes.ts';
let te = new TextEncoder;
let [pt, key, iv] = [
te.encode('January February'),
te.encode('GHIJKLMNOPQRSTUV'),
te.encode('0123456789ABCDEF')
];
// default is no padding, omit last argument if needed
let cipher = new Cbc(Aes, key, iv, Padding.PKCS7);
let ct = btoa(String.fromCharCode(...cipher.encrypt(pt)));
console.log(ct);