英文:
decrypting string with forge AES only yields some of the text
问题
以下是您提供的 JavaScript 代码的翻译部分:
seed = 'hi';
text = 'KQdciM892XEZXYC+jm4sWsijh/fQ4z/PRlpIHQG/+fM=';
function decrypt(seed, text){
md = forge.md.sha256.create();
md.update(seed);
const key = md.digest().getBytes(32);
const cypher = forge.util.createBuffer(forge.util.decode64(text), 'raw');
// 这部分看起来是正确的
console.log(cypher.toHex());
var decipher = forge.cipher.createDecipher('AES-ECB', key);
decipher.start();
decipher.update(cypher);
const result = decipher.finish();
if(result){
const out = decipher.output;
console.log(out.toHex());
const dec = forge.util.encodeUtf8(out);
console.log(dec);
}else{
console.log('Bad key.');
}
}
decrypt(seed, text);
如果您有其他问题或需要进一步的帮助,请随时提出。
英文:
I have the following javascript (see jsfiddle):
seed = 'hi';
text = 'KQdciM892XEZXYC+jm4sWsijh/fQ4z/PRlpIHQG/+fM=';
function decrypt(seed, text){
md = forge.md.sha256.create();
md.update(seed);
const key = md.digest().getBytes(32);
const cypher = forge.util.createBuffer(forge.util.decode64(text), 'raw');
// This seems right
console.log(cypher.toHex());
var decipher = forge.cipher.createDecipher('AES-ECB', key);
decipher.start();
decipher.update(cypher);
const result = decipher.finish();
if(result){
const out = decipher.output;
console.log(out.toHex());
const dec = forge.util.encodeUtf8(out);
console.log(dec);
}else{
console.log('Bad key.');
}
}
decrypt(seed, text);
The text that has been encoded is (with spaces and newlines, all):
```
## [1] 2
```
When I run the function decrypt
, I get the output:
```
## [
which is only the first 22 characters. The decryption works in R with the digest::AES
function, so I know it can work:
id = '123'
key = digest::digest(pp, algo='sha256', serialize = FALSE, raw = TRUE)
aes_e = digest::AES(key)
"KQdciM892XEZXYC+jm4sWsijh/fQ4z/PRlpIHQG/+fM=" |>
base64enc::base64decode() |>
aes_e$decrypt()
# [1] " \n\n\n```\n## [1] 2\n```\n\n"
So I suspect I misunderstand how forge works, though I have followed the documentation examples on GitHub here and here. This also happened when I tried AES-CBC.
How do I get the whole text?
答案1
得分: 1
你需要在forge端禁用默认的PKCS#7填充,即将decipher.finish()
替换为decipher.finish(() => true)
,请参考这里。然后完整的明文将被返回:
seed = 'hi';
text = 'KQdciM892XEZXYC+jm4sWsijh/fQ4z/PRlpIHQG/+fM=';
function decrypt(seed, text){
md = forge.md.sha256.create();
md.update(seed);
const key = md.digest().getBytes(32);
const cypher = forge.util.createBuffer(forge.util.decode64(text), 'raw');
// 这看起来是正确的
console.log(cypher.toHex());
var decipher = forge.cipher.createDecipher('AES-ECB', key);
decipher.start();
decipher.update(cypher);
//const result = decipher.finish();
const result = decipher.finish(() => true); // 禁用取消填充
if(result){
const out = decipher.output;
console.log(out.toHex());
const dec = forge.util.encodeUtf8(out);
console.log(dec);
}else{
console.log('Bad key.');
}
}
decrypt(seed, text);
请注意,对于像ECB这样的分组密码模式,只有在明文长度是块大小的整数倍时(对于AES是16字节),才能省略加密过程中的填充,这在这里是满足的,因为是2*16=32字节。
此外,请注意ECB是一种不安全的模式,通过快速摘要(例如SHA256)进行密钥派生存在漏洞。
编辑:
-
关于您的第一个评论:非认证的解密(例如使用AES/ECB)本身不能区分正确和不正确的结果。如果密钥正确,则返回原始明文,如果密钥不正确,则返回一个随机字节序列(或更准确地说,一个与随机字节序列无法区分的字节序列)。在密钥错误/随机字节序列的情况下,通常没有有效的填充。
启用填充后,许多库将无效的填充用作解密失败的指示器,例如forge库会抛出Bad key异常。但这并不是可靠的指示器,因为即使在密钥错误的情况下,也可能“偶然地”得到有效的填充。
如果禁用填充,则填充标准不适用。然后,只有通过返回不是原始明文而是任意字节序列或通过连带错误(例如,在发布的forge代码中,解密数据是UTF-8解码的;由于任意字节序列通常不符合UTF-8规范,随后的UTF-8解码通常会导致连带错误)才能注意到密钥错误。因此,当禁用填充时,Bad Key异常不再发生是很正常的。
R代码在密钥不正确的情况下表现方式相同:返回一个任意字节序列而没有错误消息。
为了完整起见,还有一种隐式检查数据完整性的认证加密方法(例如AES/GCM)。
-
关于您的第二个评论:我没有看过R的实现,所以不能排除填充错误的可能性!许多库为了方便/鲁棒性而使用默认填充进行块密码模式。但也有一些库不这样做,即通过专用函数分离加密和填充(类似于编码和加密)。这只是不同的哲学观点。
英文:
You need to disable the default PKCS#7 padding on the forge-side, i.e. replace decipher.finish()
by
decipher.finish(() => true)
, s. here. Then the full plaintext is returned:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
seed = 'hi';
text = 'KQdciM892XEZXYC+jm4sWsijh/fQ4z/PRlpIHQG/+fM=';
function decrypt(seed, text){
md = forge.md.sha256.create();
md.update(seed);
const key = md.digest().getBytes(32);
const cypher = forge.util.createBuffer(forge.util.decode64(text), 'raw');
// This seems right
console.log(cypher.toHex());
var decipher = forge.cipher.createDecipher('AES-ECB', key);
decipher.start();
decipher.update(cypher);
//const result = decipher.finish();
const result = decipher.finish(() => true); // disable un-padding
if(result){
const out = decipher.output;
console.log(out.toHex());
const dec = forge.util.encodeUtf8(out);
console.log(dec);
}else{
console.log('Bad key.');
}
}
decrypt(seed, text);
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/forge/1.3.1/forge.min.js"></script>
<!-- end snippet -->
Note that for block cipher modes like ECB, padding during encryption can only be omitted if the length of the plaintext is an integer multiple of the block size (16 bytes for AES), which is satisfied here with 2*16=32 bytes.
Also, be aware that ECB is an insecure mode and a key derivation via a fast digest (like SHA256) is a vulnerability.
Edit:
-
Regarding your first comment: A non-authenticated decryption (e.g. with AES/ECB) in itself does not allow to distinguish between a correct and incorrect result. If the key is correct, the original plaintext is returned, if the key is incorrect, a random byte sequence (or more precisely, a byte sequence that is indistinguishable from a random byte sequence) is returned. In the case of a wrong key / a random byte sequence, there is generally no valid padding.
With padding enabled, many libraries use an invalid padding as an indicator for a failed decryption, like the forge library throwing a Bad key exception. However, this is not a reliable indicator, because even with a wrong key, a valid padding could accidentally result.
If padding is disabled, the padding criterion does not apply. A wrong key is then only noticeable by the fact that not the original plaintext but an arbitrary byte sequence is returned or by consequential errors (e.g. in the posted forge code, the decrypted data is UTF-8 decoded; since an arbitrary byte sequence is generally not UTF-8 compliant, the subsequent UTF-8 decoding generally causes a consequential error).
Therefore, it is quite normal that the Bad Key exception no longer occurs when padding is disabled.The R code behaves the same way with an incorrect key: return an arbitrary byte sequence without error message.
For completeness, there is authenticated encryption that implicitly checks the integrity of the data (e.g. AES/GCM).
-
Regarding your second comment: I haven't looked at the R implementation, so I can't rule out a padding bug. But a missing padding should not be classified as a bug a priori!
Many libraries use a default padding for block cipher modes for convenience/robustness. However, there are also libraries that do not do this, i.e. that separate encryption and padding (similar to encoding and encryption) by dedicated functions. There are just different philosophies here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论