Golang crypto/rsa problem decrypting text using private key from PEM file - go

So i'm building a basic generator/encryptor/decryptor for RSA keys in Golang.
I have four main functions.
GenKeys() generates the key pair using rsa.GenerateKey, then writes this private key to a pem file.
GetKeys() Gets the private key from the pem file.
Encrypt() Encrypts a string manually entered into the console and then spits out the ciphertext
Decrypt() Takes in the cipher text and decrypts it, using the GetKeys function to get the private key from the pem file.
The GenKeys function works fine. And the Encrypt function works fine. Entering a string and spitting out a cipher text.
But I get crypto/rsa: decryption error when I run the decrypt flag.
Tried:
The private key is identical when I gen the key to when i Import it again from the pem file.
Ive tried the different decryption methods included in the crypto package.
I've tried just importing the ciphertext directly from the encrypt method into the decrypt, just in case there was a problem when spitting it out to console.
To note:
This worked when I only wrote the private key to memory, instead of spitting it out to a file, which tells me that something is going wrong when importing the key again from the pem file. But I cant figure out what.
Please if somebody could look through my code and tell me if im missing something I'd be eternaly grateful.
main.go
func main() {
var action = flag.String("action", "", "Whether to decrypt or encrypt")
flag.Parse()
task := *action
var err error
if task == "gen" {
//gen the priv key and write to file
err = services.GenKeys()
if err != nil {
fmt.Println("Could not generate keys:", err)
}
}
if task == "encrypt" {
//Get key from file
privateKey, err := services.GetKeys()
if err != nil {
fmt.Println("Could not retrieve key file", err)
return
}
reader := bufio.NewReader(os.Stdin)
fmt.Println("Please enter the text you would like to encrypt: ")
text, _ := reader.ReadString('\n')
cipherText, err := services.Encrypt(&privateKey.PublicKey, text)
if err != nil {
fmt.Println("Could not encrypt", err)
return
}
fmt.Printf("Encrypted message: %x", cipherText)
}
if task == "decrypt" {
//Get key from file
privateKey, err := services.GetKeys()
if err != nil {
fmt.Println("Could not retrieve key file", err)
}
reader := bufio.NewReader(os.Stdin)
fmt.Println("Please enter the cypher text you would like to decrypt: ")
text, _ := reader.ReadString('\n')
decryptedText, err := services.Decrypt(privateKey, []byte(text))
if err != nil {
fmt.Println("Could not decrypt text", err.Error())
return
}
fmt.Println("decrypted text: ", string(decryptedText))
}
}
crypto_service.go
func Encrypt(pub *rsa.PublicKey, text string) ([]byte, error) {
encryptedBytes, err := rsa.EncryptOAEP(
sha256.New(),
rand.Reader,
pub,
[]byte(text),
nil)
if err != nil {
return nil, err
}
return encryptedBytes, nil
}
func Decrypt(privKey *rsa.PrivateKey, cipherText []byte) ([]byte, error) {
decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), nil, privKey, cipherText, nil)
if err != nil {
return nil, err
}
return decryptedBytes, nil
}
gen_keys_service.go
func GenKeys() error {
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
privFile, err := os.Create("private_key.pem")
if err != nil {
return err
}
pemData := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privKey),
}
err = pem.Encode(privFile, pemData)
if err != nil {
return err
}
privFile.Close()
fmt.Println("Your keys have been written to private_key.pem")
return nil
}
func GetKeys() (*rsa.PrivateKey, error) {
file, err := os.Open("private_key.pem")
if err != nil {
return nil, err
}
defer file.Close()
//Create a byte slice (pemBytes) the size of the file size
pemFileInfo, _ := file.Stat()
var size = pemFileInfo.Size()
pemBytes := make([]byte, size)
//Create new reader for the file and read into pemBytes
buffer := bufio.NewReader(file)
_, err = buffer.Read(pemBytes)
if err != nil {
return nil, err
}
//Now decode the byte slice
data, _ := pem.Decode(pemBytes)
if data == nil {
return nil, errors.New("could not read pem file")
}
privKeyImport, err := x509.ParsePKCS1PrivateKey(data.Bytes)
if err != nil {
return nil, err
}
return privKeyImport, nil
}
Oh and heres the format of my pem file:
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEApX2AUrhtH3FHSds3JAWmBDZcSwixydY/M52WmWfqDmQDRc6X
V4FqLyiEIMSq3Z7zOR9GH4a2Ps3HPlTvJa2EGkgKDk7hVRWlocbQvRsdSk/aEZ7U
O0SgB6s0H2/9MdWsXaJHADuUJYlSgG+VH7vf5uIi4w8OKNfmHfB7uJQJByb4+8jg
rzMFgmFyEUnWOgUh3dXtdV1XD5BPQiDk2D8vYR8JUjK9uWHVMaR2y51uK3vNZHmR
/ce1g36Gzhl5aRfUqJ8gWLCsJ42VEDHznz04YvrH/DRrcuSb/65JlpQMOKr9wdf/
/sILXkeEylifeZoUENRbgIz5UCyyQtTrfCMcDwIDAQABAoIBAQCBVx41CKlY2rsh
0TA+eh+vySDHECuMGWPaHn92tE2tI6OfKoEERkRMSTlDNxJoFfP4oCW5PBsx6Lih
UdZ7Nys5MzaQyju4uStzziSUuB1jWBeSj/VFwTPl6nvZWjike2C06cDeQLJfx/zw
HsWJQNgU5obnuXuSlnLm0W6Q6zpHGH/GdtS6O5kLjLySgb87xDTivdKEH5iqUNRe
fs6WTJpsKsT92lT/AcO/QUuw7sK/GSW2spnzO8xF4ZdQhjj72NkWqa/K/ncfKhuk
eI/+1HPvSru+nOJts6j3Xd98EQbIDBVZLArl6gB49B6UsEogAYgRJAs5mIIHzxRS
YfiQckzRAoGBAMijQPc+ygZ9pTNLOCi7zHQtHHOj8lMGuqQKTFsDIxVGI7Iy6tm/
CQbbpZiQiDoskCSJq3pWcydu+csDmdq/PrTJ4yuJMAgUaOItvuNu5vS3nOGPiJBl
0zDERW2GA0jcE4STreVM5GR3kOgNG8EIbmjVC1eQqhKK7a0+kPbYqYBnAoGBANMn
e6VcmL19WNIEYHyXLdCOaPj7LOMmAA2SNRA7V4YkaXbgRSHmnHboHEVzytSJH9gY
mmZ9+8dkq9n+ODi1vn2hCR9TIJHapn8aWHOqgnZesJ2xThV78S8Kp8jZD4lx8Ejy
5t3TMVIZ6hQXWfkQjpKs3QAXJszrZR0Cb0sJOp4ZAoGAVb3mmoKbxXBWc/g2NLok
4pUQrp6mhTK/+jBodxB9Iq9Ju/KkiI9qJSMivnMttGas5uBqhqqBJZ34Y/jS/Dd0
bcBFM6q5+wn11IqmI/2V94isI3y6aLMLMLD/1pFJwA5EzQhrbS2SARlTtMc9UB3x
SR2abj5lX+09KycsvkDTFwsCgYEAhG4b509W4GlQHo/uL9XlPwtCHP3zXhdEKz7K
lv6Q2ynVF2B7G1L2SME0kf6YdIn39eEgXS2gjqK+AX+mpTKfYU1xWjsZj0CR8d+F
IC4Nms2yeYRtU54jWvyU1KwGSY6zRB1L3E4LfdGQ9L7ciA/xS16tUNQ0Wos8JGCV
inskT2kCgYBg4zzkUAmQ75hlRsM0r7ezrx4xt8GHrlxwOTeQYgVcymiSSMXVITNI
ByBfs+wqV7PrXjM69xPdV/CHczFcA7g/M0o0CJZhWead403knjZmGFlZypvWskOT
aK4nArJ0zaS7FGhCe24QORYZPGjCzg7dLKhKDvK1PB4ogRVo4dDckA==
-----END RSA PRIVATE KEY-----

In the encryption part the ciphertext is displayed hex encoded. If it is entered hex encoded in the decryption part, it must be hex decoded accordingly, which is missing in the posted code. The hex decoding can be done with the hex package.
Also, as mentioned in Peter's comment, the trailing newline must be removed, e.g. with strings.TrimSuffix(). Alternatively, fmt.Scan() or bufio.Scanner can be used instead of bufio.Reader. The last two options do not return the trailing newline.
Possible implementation (for simplicity without exception handling):
...
var text string
fmt.Println("Please enter the ciphertext you would like to decrypt: ")
fmt.Scan(&text)
textHexDec, _ := hex.DecodeString(text)
decryptedText, _ := Decrypt(privateKey, textHexDec)
fmt.Println("Decrypted text: ", string(decryptedText))
...

Related

How to write *PrivateKey type variable to a file in golang? [duplicate]

This question already has answers here:
Save and load crypto/rsa PrivateKey to and from the disk
(3 answers)
Use golang to get RSA key the same way openssl genrsa
(1 answer)
Closed 4 months ago.
I have generated a RSA private key (using crypto/rsa) and corresponding public key in go. Now I want to write them to two files (each one to a file), but WriteString and Write functions are specific to string and []byte variable. Also instructions like String(privateKey) generate error. How I can write these keys to the files?
You would need pem.Encode. An example can be seen in maplepie/rsa#savePrivateKey():
func (p *PemKey) savePrivateKey(privateKey *rsa.PrivateKey, filename string) error {
raw := x509.MarshalPKCS1PrivateKey(privateKey)
block := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: raw,
}
file, err := os.Create(filename)
if err != nil {
return err
}
err = pem.Encode(file, block)
if err != nil {
return err
}
return nil
}
Same idea for public key:
func (p *PemKey) savePublicKey(publicKey *rsa.PublicKey, filename string) error {
raw, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
block := &pem.Block{
Type: "PUBLIC KEY",
Bytes: raw,
}
file, err := os.Create(filename)
if err != nil {
return err
}
err = pem.Encode(file, block)
if err != nil {
return err
}
return nil
}

Encrypting/Decrypting JWE with Jose in Go

I'm trying to create a JWE decryption function but having trouble determining how to use the Go Jose interface for doing so. I've factored the encryption using passphrase (I prefer a passphrase for this use case):
token := jwt.NewWithClaims(
jwt.SigningMethodHS256,
claims,
)
ss, err := token.SignedString("thisisatestpassphraserighthere")
if err != nil {
panic("COULD_NOT_GENERATE")
return
}
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
publicKey := &privateKey.PublicKey
encrypter, err := jose.NewEncrypter(
jose.A128CBC_HS256,
jose.Recipient{Algorithm: jose.RSA_OAEP, Key: publicKey},
nil,
)
if err != nil {
return
}
object, err := encrypter.Encrypt([]byte(ss))
if err != nil {
errRes = s.Error(codes.Internal, err, nil)
return
}
key, err := object.CompactSerialize()
if err != nil {
errRes = s.Error(codes.Internal, err, nil)
return
}
fmt.Println(key)
The above code creates a JWT, encodes it, compacts it and returns the key. It's not however totally clear how to decrypt it with the passphrase now.
There is an example for JWE on the Jose docs: https://godoc.org/gopkg.in/square/go-jose.v2#example-Encrypter--Encrypt
So I've factored this:
object, err = jose.ParseEncrypted(Key)
if err != nil {
panic(err)
}
decrypted, err := object.Decrypt(...)
Inside the ellipses I'm not sure what to put though. I can't seem to determine how to pass in a key based on passphrase.
I was doing a few things wrong it seems. First of all rsa.GenerateKey uses a randomized value. This was totally wrong :-p the following is how you can encrypt a JWT into a JWE using a token:
rcpt := jose.Recipient{
Algorithm: jose.PBES2_HS256_A128KW,
Key: "mypassphrase",
PBES2Count: 4096,
PBES2Salt: []byte{ your salt... },
}
enc, err := jose.NewEncrypter(jose.A128CBC_HS256, rcpt, nil)
if err != nil {
panic("oops")
}
jewPlaintextToken, err := enc.Encrypt(jwtToken)
if err != nil {
panic("oops")
}
key, err := object.CompactSerialize()
if err != nil {
panic("oops")
}
This is how you decrypt:
// Decrypt the receive key
jwe, err := jose.ParseEncrypted(jewPlaintextToken)
if err != nil {
panic("oops")
}
decryptedKey, err := jwe.Decrypt("mypassphrase")
if err != nil {
panic("oops")
}
If anyone sees any major problems/security issues with this method, please mention it.
Inside the ellipses I'm not sure what to put though. I can't seem to determine how to pass in a key based on passphrase.
From the JWE example in the documentation, you must pass the private key. See the below part for decryption
https://godoc.org/gopkg.in/square/go-jose.v2#JSONWebEncryption.Decrypt
// Generate a public/private key pair to use for this example.
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// Parse the serialized, encrypted JWE object. An error would indicate that
// the given input did not represent a valid message.
object, err = ParseEncrypted(serialized)
if err != nil {
panic(err)
}
// Now we can decrypt and get back our original plaintext. An error here
// would indicate the the message failed to decrypt, e.g. because the auth
// tag was broken or the message was tampered with.
decrypted, err := object.Decrypt(privateKey)
if err != nil {
panic(err)
}
fmt.Printf(string(decrypted))

unstable decryption, sometimes got cipher: message authentication failed

I'm trying to create E2E Encryption for my software, but the decryption are very unstable, sometime can successfully decrypt, sometime got cipher: message authentication failed, here's my encrypt & decrypt code
func Encrypt(data []byte, passphrase string) ([]byte, error) {
// create aes.NewCipher from hashed md5 passphrase
block, _ := aes.NewCipher([]byte(createHash(passphrase)))
// NewGCM returns the given 128-bit, block cipher wrapped in
// Galois Counter Mode with the standard nonce length.
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// initialize slice with length of nonce that must be passed to Seal and Open.
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
ciphertext := gcm.Seal(nonce, nonce, data, nil)
return ciphertext, nil
}
func Decrypt(data []byte, passphrase string) ([]byte, error) {
// create md5 byte slice
key := []byte(createHash(passphrase))
// just `reverse` algorithm with passphrase until return
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
the encrypted binary value are transferred via http :
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
decrypt, err := Decrypt(body, r.Passphrase)
what i already try is to check, is ioutil.ReadAll read content correctly, or something wrong with decryptor
sorry, the problem was not in encryption/decryption, but in http server for transferring the chipertext, and already fixes now https://github.com/codenoid/GoTral-Server/commit/493c7f654753cae36f074c1c5f382953e227d295

Golang pgp without secring

I have an app that uses gpg secret keys and prompts for a password to read it. Here's the way I do that (based on an example I found elsewhere:
func Decrypt(publicKeyring string, secretKeyring string, key string, password string) (string, error) {
var entity *openpgp.Entity
var entityList openpgp.EntityList
keyringFileBuffer, err := os.Open(secretKeyring)
if err != nil {
return "", err
}
defer keyringFileBuffer.Close()
entityList, err = openpgp.ReadKeyRing(keyringFileBuffer)
if err != nil {
return "", err
}
entity = entityList[0]
passphraseByte := []byte(password)
entity.PrivateKey.Decrypt(passphraseByte)
for _, subkey := range entity.Subkeys {
subkey.PrivateKey.Decrypt(passphraseByte)
}
dec, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", err
}
// Decrypt it with the contents of the private key
md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), entityList, nil, nil)
if err != nil {
return "", err
}
bytes, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil {
return "", err
}
decStr := string(bytes)
return decStr, nil
}
The assumption made here is that the user has a KeyRin which is passed, and the default value for this is the secring, like so:
viper.SetDefault("gpgsecretkeyring", home+"/.gnupg/secring.gpg")
However,
I was getting reports that some users on macs were struggling to get the app working, and the reason was they didn't know how to define the secring.
It seems newer versions of GnuPG have deprecated the secring.
https://www.gnupg.org/faq/whats-new-in-2.1.html#nosecring
I have no idea how to read the secret key using golang.org/x/crypto/openpgp at this point. Are there any examples of the best way to do this?
GnuPG 2.1 introduced two changes:
Merging the secring.gpg into the pubring.gpg file, you should be able to read the secret keys from the pubring.gpg file.
For new installations, the new keybox format is used, which is not supported by the go library (as of today, at least). Old installations (thus, with keyrings in the old format), are not automatically merged.
If you want to use GnuPG's keyring, directly call GnuPG. If you want to use Go's library, don't mess with GnuPG's keyring files and store your own copy of the keys.
I got sick of dealing with this, so I've decided it's easier to just shell out to gpg -dq from the os.Exec. Sample:
package gpg
import (
"bytes"
"encoding/base64"
"os/exec"
)
func Decrypt(key string) (string, error) {
var cmd exec.Cmd
var output bytes.Buffer
gpgCmd, err := exec.LookPath("gpg")
if err != nil {
return "", err
}
cmd.Path = gpgCmd
cmd.Args = []string{"--decrypt", "--quiet"}
dec, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", err
}
// return the reader interface for dec (byte array)
d := bytes.NewReader(dec)
// pipe d to gpg commands stdin
cmd.Stdin = d
cmd.Stdout = &output
if err := cmd.Run(); err != nil {
return "", err
}
// return the output from the gpg command
return output.String(), nil
}

Load encrypted PKCS1 private key from file

I have a PKCS1 private key in a file and I load it using
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
Then, I try to convert it into private key object
block, _ := pem.Decode(b)
der, err := x509.DecryptPEMBlock(block, []byte("qwerty"))
if err != nil {
return nil, err
}
bytes := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: der})
return x509.ParsePKCS1PrivateKey(bytes)
But this code throws exception in DecryptPEMBlock
x509: no DEK-Info header in block
I didn't find any documentation about this in golang
I made a mistake with my private key file and here is a working code
func GetPrivateKey(path string) (*rsa.PrivateKey, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
block, _ := pem.Decode(b)
der, err := x509.DecryptPEMBlock(block, []byte(*PrivateKeyPassword))
if err != nil {
return nil, err
}
return x509.ParsePKCS1PrivateKey(der)
}
P.S. Go does have a package to decrypt PKCS1 private keys, but does not have for PKCS8.
Go does not have package to decode PKCS files it seems
Checkout this link for more details PKCS
I cant find a package to decode PKCS1 keys
Use this package to decode the PKCS8 files, there are some packages to decode PKCS8, PKCS10, PKCS12 but not for PKCS1

Resources