How to decrypt an AES encrypted transport stream segment with Golang? - go

I know using openssl (tested with OpenSSL 1.1.0g), the following stanza works to decrypt enc.ts, mimetype: video/MP2T, to a ffplay playable clear.ts h264 segment:
openssl aes-128-cbc -d -in enc.ts -out clear.ts -iv 353833383634 -K 9e8c69bcaafa6b636e076935e29986b5 -nosalt
Though with Golang's https://golang.org/pkg/crypto/cipher/#NewCBCDecrypter I'm very confused how the hexadecimal key and iv are set as byte slices, whether block sizes are a factor and how to load and write out the file.
I have tried:
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
"io/ioutil"
)
func checkerror(err error) {
if err != nil {
panic(err)
}
}
func main() {
key, err := hex.DecodeString("9e8c69bcaafa6b636e076935e29986b5")
checkerror(err)
iv, err := hex.DecodeString("353833383634")
checkerror(err)
ciphertext, err := ioutil.ReadFile("enc.ts")
checkerror(err)
block, err := aes.NewCipher(key)
checkerror(err)
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
fmt.Printf("%s\n", ciphertext)
}
But that results in a panic: cipher.NewCBCDecrypter: IV length must equal block size. What am I missing?

Your iv is indeed too short so the openssl just padded zero for you:
openssl aes-128-cbc -d -in enc.ts -out clear.ts -iv 353833383634 -K 9e8c69bcaafa6b636e076935e29986b5 -nosalt -P
key=9E8C69BCAAFA6B636E076935E29986B5
iv =35383338363400000000000000000000

Related

Can't verify RSA-PSS signatures across openssl/golang 1.18 [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 months ago.
Improve this question
I'm trying to generate an RSA PSS signature with openssl in a bash script and verify it in a go microservice. Verification fails across openssl/go.
Generate the keys and signature (signature.bin) in openssl/bash...
openssl genrsa -out key.pem 4096
openssl rsa -in key.pem -pubout > key.pub
truncate -size=16 zeroes.bin
openssl dgst -sign key.pem -keyform PEM -sha512 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:64 -out signature.bin -binary zeroes.bin
Use openssl to verify signature (ok)...
openssl dgst -verify key.pub -keyform PEM -sha512 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:64 -signature signature.bin -binary zeroes.bin
Verified OK
Go can create and verify its own RSA-PSS, but fails on the signature from openssl...
go run main.go
Verified go signature.
panic: crypto/rsa: verification error
goroutine 1 [running]:
main.main()
/root/taas/applications.security.amber.core-services/pss/x/main.go:51 +0x339
exit status 2
openssl also fails to verify the go signature...
openssl dgst -verify key.pub -keyform PEM -sha512 -signature signature.**go**.bin -binary zeroes.bin
Verification Failure
main.go
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
)
func main() {
privatePem, _ := ioutil.ReadFile("key.pem")
privateBlock, _ := pem.Decode(privatePem)
if privateBlock == nil {
panic("Failed to decode private pem")
}
privateKey, err := x509.ParsePKCS1PrivateKey(privateBlock.Bytes)
if err != nil {
panic(err)
}
h := sha512.New()
h.Write(make([]byte, 16))
zeroes := h.Sum(nil)
signature, err := rsa.SignPSS(rand.Reader, privateKey, crypto.SHA512, zeroes, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash, // should be 64 for sha512
})
if err != nil {
panic(err)
}
ioutil.WriteFile("signature.go.bin", signature, 0600)
err = rsa.VerifyPSS(&privateKey.PublicKey, crypto.SHA512, zeroes, signature, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
})
if err != nil {
panic(err)
}
fmt.Println("Verified go signature.")
opensslSignature, _ := ioutil.ReadFile("signature.bin")
err = rsa.VerifyPSS(&privateKey.PublicKey, crypto.SHA512, zeroes, opensslSignature, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
})
if err != nil {
panic(err)
}
fmt.Println("Not getting here -- crypto/rsa: verification error")
}

How to encrypt a file so that OpenSSL can decrypt it without providing the IV manually

I want to encrypt a file with AES the same way openssl enc command does, so when I want to decrypt it there won't be need for providing IV manually.
In order to decrypt an AES encrypted file you need both key and IV. IV is not a secret and is usually store at the encrypted file.
OpenSSL uses a key derivation function to generate these two using the provided password and a random salt. then after encryption it stores the salt at the header of the file with Salted__ prefix, so at the decryption it could use it along with the password to produce the same key and IV.
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"io"
"os"
"golang.org/x/crypto/pbkdf2"
)
func main() {
keySize := 32;
// its only for demonstration purpose
password := []byte("TESTPASSWORD1234TESTPASSWORD1234");
bReader, err := os.Open("doc.docx")
defer bReader.Close();
if err != nil {
panic(err)
}
salt := make([]byte, 8)
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
panic(err)
}
computed := pbkdf2.Key(password, salt, 10000, keySize + aes.BlockSize , sha256.New)
key := computed[:keySize]
iv := computed[keySize:]
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
stream := cipher.NewOFB(block, iv)
bWriter, err := os.Create("doc-encrypted.docx")
if err != nil {
panic(err)
}
defer bWriter.Close()
prefix := []byte("Salted__");
header := append(prefix[:], salt...);
bWriter.Write(header)
sWriter := &cipher.StreamWriter{S: stream, W: bWriter}
if _, err := io.Copy(sWriter, bReader); err != nil {
panic(err)
}
}
and you can decrypt it with openssl enc -in doc-encrypted.docx -out doc-decrypted.docx -d -aes-256-ofb -pbkdf2 -pass pass:TESTPASSWORD1234TESTPASSWORD1234

How I can decode aes-256-cfb

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)
}
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)
}

Encrypted a file with AES but can't decrypt it with OpenSSL (bad magic number)

I have encrypted a file using this code.
block, err := aes.NewCipher([]byte("TESTPASSWORD1234TESTPASSWORD1234"))
if err != nil {
panic(err)
}
bReader, err := os.Open("doc.docx")
if err != nil {
panic(err)
}
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
var out bytes.Buffer
writer := &cipher.StreamWriter{S: stream, W: &out}
if _, err := io.Copy(writer, bReader); err != nil {
panic(err)
}
if os.WriteFile("doc-encrypted.docx", out.Bytes(), 0644) != nil {
panic(err)
}
and when I try to decrypt it using this command
openssl enc -in doc-encrypted.docx -out doc-decryted.docx -d -aes-256-ofb
it gives the error bad magic number
Your OpenSSL statement is missing the specification of key and IV. For decryption, the following OpenSSL statement is required:
openssl enc -in doc-encrypted.docx -out doc-decryted.docx -d -aes-256-ofb -K 5445535450415353574f5244313233345445535450415353574f524431323334 -iv 00000000000000000000000000000000
The -K option specifies the hex encoded key, and -iv specifies the hex encoded IV, s. enc.
With this change, the ciphertext generated with the Go code can be decrypted with the OpenSSL statement.
Keep in mind that the use of a static IV is insecure. Typically, a random IV is generated for each encryption. This is not secret and is usually concatenated with the ciphertext: iv|ciphertext so that it is available during decryption. See the documentation for NewOFB for an example (without file I/O).

Go encryption differs from Ruby encryption using same key and iv

I have the following Ruby code:
require 'base64'
require 'openssl'
data = '503666666'
key = '4768c01c4f598828ef80d9982d95f888fb952c5b12189c002123e87f751e3e82'
nonce = '4eFi6Q3PX1478767\n'
nonce = Base64.decode64(nonce)
c = OpenSSL::Cipher.new('aes-256-gcm')
c.encrypt
c.key = key
c.iv = nonce
result = c.update(data) + c.final
tag = c.auth_tag
puts Base64.encode64(result + tag) # => J3AVfNG84bz2UuXcfre7LVjSbMpX9XBq6g==\n
that I'm trying to replicate in Golang.
Here's what I have so far:
package main
import (
"fmt"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/hex"
)
func main() {
data := []byte("503666666")
key, err := hex.DecodeString(`4768c01c4f598828ef80d9982d95f888fb952c5b12189c002123e87f751e3e82`)
if err != nil {
panic(err)
}
nonceB64 := "4eFi6Q3PX1478767\n"
nonce, err := base64.StdEncoding.DecodeString(nonceB64)
if err != nil {
panic(err)
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err.Error())
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
ciphertext := aesgcm.Seal(nil, nonce, data, nil)
fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(ciphertext))
}
However the outcome from the Go version is:
+S52HGbLV1xp+GnF0v8VNOqc5J2GY2+SqA==
vs.
J3AVfNG84bz2UuXcfre7LVjSbMpX9XBq6g==\n
Why am I getting different results?
Thanks,
The AES 256 cipher requires a 32 byte key. The Ruby code is setting the key to a 64 byte string consisting of hexadecimal digits. OpenSSL is truncating the string to 32 bytes before use (change key to '4768c01c4f598828ef80d9982d95f888' in the Ruby code and you'll get the same output).
The Go code however is hex decoding the key before use, converting the 64 hexadecimal digits to the 32 bytes required for the key.
If you want to change the Go code so that it matches the Ruby result, then you'll need to truncate the key and remove the hex decoding step:
key := []byte("4768c01c4f598828ef80d9982d95f888")
However, I'd argue that the key handling in the Go version of the code is better. If you want to change the Ruby version to match the Go version, you can hex decode the key before use:
key = [key].pack('H*')

Resources