2

How I can decode aes-256-cfb?

I have file encoded by aes-256-cfb, when I use openssl command

openssl enc -d -aes-256-cfb -salt -pbkdf2 -pass file:encpass -out x.txz -in encpkg

this file is decrypted without any problem,but when I try to decrypt this file by golang, I always get incorrect file, I don't know what my problem is and I hope to find help

my code:

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha256"
    "log"
    "os"
)

func main() {
    fiencpkg, err := os.ReadFile("encpkg")
    if err != nil {
        log.Println(err)
        os.Exit(1)
    }

    fiencpass, err := os.ReadFile("encpass")
    if err != nil {
        log.Println(err)
        os.Exit(1)
    }

    keyb := sha256.Sum256(fiencpass)
    block, err := aes.NewCipher(keyb[:])
    if err != nil {
        panic(err)
    }

    if len(fiencpkg) < aes.BlockSize {
        panic("data too short")
    }

    iv := fiencpkg[:aes.BlockSize]
    decdata := fiencpkg[aes.BlockSize:]
    stream := cipher.NewCFBDecrypter(block, iv)

    stream.XORKeyStream(decdata, fiencpkg[aes.BlockSize:])
    os.WriteFile("x_go.txz", decdata, 0777)

}
isherwood
  • 58,414
  • 16
  • 114
  • 157
Unknown
  • 401
  • 4
  • 15

1 Answers1

1

The -pbkdf2 option in the OpenSSL statement causes the PBKDF2 key derivation to be used:
During encryption, a random 8 bytes salt is generated, and together with the password, the key and IV are determined using this key derivation.
Since the salt is needed for decryption, the OpenSSL statement concatenates salt and ciphertext and indicates this with the prefix Salted__.

Thus, during decryption, salt and ciphertext must first be separated:

salt := fiencpkg[8:16]
ciphertext := fiencpkg[16:]

Then key and IV can be derived via PBKDF2 using e.g. the pbkdf2 package:

keyIv := pbkdf2.Key(fiencpass, salt, 10000, 48, sha256.New)
key := keyIv[0:32]
iv := keyIv[32:48]

Note the OpenSSL default values 10000 and SHA256 for iteration count and digest. Since the encryption was done with AES-256-CFB 48 bytes have to be generated (32 bytes for the key, 16 bytes for the IV).

After determining key and IV, decryption can be performed as usual.


Full code:

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha256"
    "log"
    "os"
    
    "golang.org/x/crypto/pbkdf2"
)

func main() {
    fiencpkg, err := os.ReadFile("encpkg")
    if err != nil {
        log.Println(err)
        os.Exit(1)
    }

    salt := fiencpkg[8:16]
    ciphertext := fiencpkg[16:]

    fiencpass, err := os.ReadFile("encpass")
    if err != nil {
        log.Println(err)
        os.Exit(1)
    }

    keyIv := pbkdf2.Key(fiencpass, salt, 10000, 48, sha256.New)
    key := keyIv[0:32]
    iv := keyIv[32:48]

    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(ciphertext, ciphertext)
    os.WriteFile("x_go.txz", ciphertext, 0777)
}
Topaco
  • 40,594
  • 4
  • 35
  • 62
  • thank you very much for this wonderful explanation, but I still get incorrect file – Unknown Aug 24 '22 at 10:06
  • file size same as file decrypted by openssl – Unknown Aug 24 '22 at 10:07
  • @TopacoI - I think the problem is when creating the tar file thank you very much for help – Unknown Aug 24 '22 at 16:03
  • @Unknown - I cannot reproduce this problem, neither on my local machine nor online in the Go playground, s. https://go.dev/play/p/DiOz1PdxjCW (the data applied are that of key and openssl-ciphertext). If you use the same code, this points to a problem with your data. Post test data, a password and an openssl-ciphertext (both directly, e.g. base64 encoded, or as files). Maybe that's how we find the reason. – Topaco Aug 24 '22 at 16:46
  • is decrypt files same as text or there is another way, my problem I can't get test example because I not created encrypted file – Unknown Aug 24 '22 at 18:20
  • for example when I decrypt by openssl I get .tar file when I extract this file I get director under it set of files – Unknown Aug 24 '22 at 18:27
  • 1
    @Unknown - The decryption always returns the original file, no matter if it is a txt- or a tar-file. So for your test (with a *test* password file) you can use a txt- or a tar-file at will and encrypt it with openssl. The decryption with openssl or with the Go code will then give the *original* file. If there are any problems, you can share the three files to help you troubleshoot. – Topaco Aug 25 '22 at 15:45
  • Hello @Topaco sometimes the encrypted file is not decrypted correctly, even though it is decrypted by openssl correctly, and I don't know what my problem, I hope to have a reason to fix this problem, Thanks – Unknown Aug 29 '22 at 09:34
  • @Unknown - With this information no troubleshooting is possible. I have already described what you have to do: Provide the three files with which the problem is reproducible. Only then a troubleshooting is possible. – Topaco Aug 29 '22 at 09:43
  • https://drive.google.com/file/d/1SakXfM1XqT6u-BLSWp9-OopO-RsSMpfs/view?usp=sharing my example with encrypt and decrypt simple script – Unknown Aug 29 '22 at 12:08
  • 1
    @Unknown - The problem is caused by your password file (*pass*). At the end of the password there is a line break (`0x0a`). OpenSSL ignores the line break, the Go code does not, because the password is read with `ReadFile()`. The easiest fix is to remove the line break in the password file. Alternatively, the Go code can be implemented more resiliently by reading only the first line of the password file (without line break, of course). – Topaco Aug 29 '22 at 18:00
  • what about password be like that 你好世界, I think this will also generate incorrect file – Unknown Aug 29 '22 at 18:22
  • 1
    @Unknown - Why should this not work? Characters are ultimately mapped to byte sequences via an encoding, e.g. in the case of a UTF-8 encoding, the 4 characters correspond to E4BDA0 E5A5BD E4B896 E7958C, as you can verify in a UTF-8 table. So OpenSSL and Go, if using the same file, would apply the same bytes as password. – Topaco Aug 29 '22 at 18:59