DecryptPKCS1v15SessionKey in Go
DecryptPKCS1v15SessionKey decrypts a session key using RSA and the padding scheme from PKCS#1 v1.5. If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. It returns an error if the ciphertext is the wrong length or if the ciphertext is greater than the public modulus. Otherwise, no error is returned. If the padding is valid, the resulting plaintext message is copied into key. Otherwise, key is unchanged. These alternatives occur in constant time. It is intended that the user of this function generate a random session key beforehand and continue the protocol with the resulting value. This will remove any possibility that an attacker can learn any information about the plaintext. See “Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS #1”, Daniel Bleichenbacher, Advances in Cryptology (Crypto '98).
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"os"
)
func main() {
// crypto/rand.Reader is a good source of entropy for blinding the RSA
// operation.
rng := rand.Reader
// The hybrid scheme should use at least a 16-byte symmetric key. Here
// we read the random key that will be used if the RSA decryption isn't
// well-formed.
key := make([]byte, 32)
if _, err := io.ReadFull(rng, key); err != nil {
panic("RNG failure")
}
rsaCiphertext, _ := hex.DecodeString("aabbccddeeff")
if err := DecryptPKCS1v15SessionKey(rng, rsaPrivateKey, rsaCiphertext, key); err != nil {
// Any errors that result will be “public” – meaning that they
// can be determined without any secret information. (For
// instance, if the length of key is impossible given the RSA
// public key.)
fmt.Fprintf(os.Stderr, "Error from RSA decryption: %s\n", err)
return
}
// Given the resulting key, a symmetric scheme can be used to decrypt a
// larger ciphertext.
block, err := aes.NewCipher(key)
if err != nil {
panic("aes.NewCipher failed: " + err.Error())
}
// Since the key is random, using a fixed nonce is acceptable as the
// (key, nonce) pair will still be unique, as required.
var zeroNonce [12]byte
aead, err := cipher.NewGCM(block)
if err != nil {
panic("cipher.NewGCM failed: " + err.Error())
}
ciphertext, _ := hex.DecodeString("00112233445566")
plaintext, err := aead.Open(nil, zeroNonce[:], ciphertext, nil)
if err != nil {
// The RSA ciphertext was badly formed; the decryption will
// fail here because the AES-GCM key will be incorrect.
fmt.Fprintf(os.Stderr, "Error decrypting: %s\n", err)
return
}
fmt.Printf("Plaintext: %s\n", string(plaintext))
}