Is it possible to decrypt binary .PGP file with Go's "golang.org/x/crypto/openpgp" library?
The following code works with an armored ASCII encoded file but throws EOF error with a binary PGP file. Is there a similar function to armor.Decode() that allows for the binary file to be decrypted?
func DecryptFile(encryptedFilePath string, decryptedFilePath string) error {
pubKey := decodePublicKey()
privKey := decodePrivateKey()
entity := createEntityFromKeys(pubKey, privKey)
file := readFile(encryptedFilePath)
block, err := armor.Decode(file)
if err != nil {
log.Printf("Error reading OpenPGP Armor: %s", err)
return err
}
if block.Type != "Message" {
log.Println("Invalid message type")
return err
}
var entityList openpgp.EntityList
entityList = append(entityList, entity)
messageDetails, err := openpgp.ReadMessage(block.Body, entityList, nil, nil)
if err != nil {
log.Printf("Error reading message: %s", err)
return err
}
compressed, err := gzip.NewReader(messageDetails.UnverifiedBody)
if err != nil {
log.Printf("Invalid compression level: %s", err)
return err
}
defer compressed.Close()
buffer := bytes.NewBuffer(nil)
n, err := io.Copy(buffer, compressed)
if err != nil {
log.Printf("Error reading encrypted file: %s", err)
return err
}
err = ioutil.WriteFile(decryptedFilePath, buffer.Bytes(), 0644)
if err != nil {
log.Println(err)
return err
}
log.Printf("Decrypted %d bytes\n", n)
return nil
}
I encountered the same exact problem with PGP, also got EOF on my first trial.
The encrypted file is in binary(.pgp)
The private/public key in key ring armored file(.asc) like this:
----BEGIN PGP PUBLIC KEY BLOCK-----
-----END PGP PRIVATE KEY BLOCK-----
Here is the code that I use:
keyRing, err := os.Open("keyArmoredFile.asc")
if err != nil {
log.Fatal(err)
}
entityList, err := openpgp.ReadArmoredKeyRing(keyRing)
if err != nil {
log.Fatal(err)
}
entity := entityList[0]
passphraseByte := []byte("password")
err = entity.PrivateKey.Decrypt(passphraseByte)
if err != nil {
log.Fatal(err)
}
for _, subkey := range entity.Subkeys {
subkey.PrivateKey.Decrypt(passphraseByte)
}
encryptedContent, err := os.Open("encryptedFile.pgp")
if err != nil {
log.Fatal(err)
}
md, err := openpgp.ReadMessage(encryptedContent, entityList, nil, nil)
if err != nil {
log.Fatal(err)}
bytes, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil {
log.Fatal(err)
}
// decryption result
decStr := string(bytes)
Reference:
https://gist.github.com/stuart-warren/93750a142d3de4e8fdd2
Related
i want to decrypt tls payloads with private key but I take an error.
This snippet is read the pcap's payload and decrypt.
Could not decrypt text crypto/rsa: decryption error
func ReadFromPcap(path string) {
handle, err := pcap.OpenOffline(path)
if err != nil {
log.Fatal(err)
}
defer handle.Close()
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
appLayer := packet.ApplicationLayer()
if appLayer != nil {
privateKey, err := GetKeys()
fmt.Println(privateKey)
if err != nil {
fmt.Println("Could not retrieve key file", err)
}
decryptedText, err := Decrypt(privateKey, appLayer.Payload())
if err != nil {
fmt.Println("Could not decrypt text", err.Error())
return
}
fmt.Println("decrypted text: ", string(decryptedText))
}
}
}
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
}
I am creating a picture using jpeg.Encode and want to send it. How can I avoid creating an intermediate file?
Create and save file.
// ...
outFile, err := os.Create("./images/" + name + ".jpg")
if err != nil {
log.Fatal(err)
os.Exit(-1)
}
defer outFile.Close()
buff := bufio.NewWriter(outFile)
err = jpeg.Encode(buff, background, &jpeg.Options{Quality: 90})
if err != nil {
log.Fatal(err)
os.Exit(-1)
}
err = buff.Flush()
if err != nil {
log.Fatal(err)
os.Exit(-1)
}
Send file.
file, err := os.Open("./images/" + name + ".jpg")
if err != nil {
log.Fatal(err)
}
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("photo", file.Name())
if err != nil {
return nil, err
}
io.Copy(part, file)
if err = writer.Close(); err != nil {
return nil, err
}
resp, err := http.Post(uploadURL, writer.FormDataContentType(), body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
How can I send a photo without saving it on the server?
Simply pass the part target to jpeg.Encode():
// ...
part, err := writer.CreateFormFile("photo", file.Name())
if err != nil {
return nil, err
}
err = jpeg.Encode(part, background, &jpeg.Options{Quality: 90})
if err != nil {
return nil, err
}
if err = writer.Close(); err != nil {
return nil, err
}
resp, err := http.Post(uploadURL, writer.FormDataContentType(), body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
How do I make this not require a file seek? Basically, I am doing a hash, and then re-reading the file. This is not optimal. How can I optimize by either using TeeReader or another method to read in chunks so Hashing and Writing content can happen without duplication of reading.
Also, do I need to specify content length myself?
// PUT method
func (c *Client) PutFileOld(filename string, noLen bool) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
hasher := md5.New()
if _, err := io.Copy(hasher, file); err != nil {
log.Fatal("Could not compute MD5")
}
// Lazy way to go back to the beginning since the reader has consumed our bytes
// and we have to compute the hash
file.Seek(0, 0)
c.MD5 = hex.EncodeToString(hasher.Sum(nil)[:16])
log.Printf("Uploading to: %s", fmt.Sprintf("%s/%s", c.baseURL, filename))
baseURL, err := url.Parse(fmt.Sprintf("%s/%s", c.baseURL, filename))
if err != nil {
return err
}
log.Printf("MD5: %s - file: %s\n", c.MD5, filename)
req, err := http.NewRequest(http.MethodPut, baseURL.String(), bufio.NewReader(file))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("Content-Md5", c.MD5)
fi, _ := file.Stat()
// Not sure if this is needed, or if Go sets it automatically
req.ContentLength = fi.Size()
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
dump, err := httputil.DumpResponse(res, true)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q\n", dump)
c.StatusCode = res.StatusCode
defer res.Body.Close()
return nil
}
I am following this example https://www.youtube.com/watch?v=eVlxuST7dCA to make a jwt auth. When I run the code below I get "Key is invalid" error. When I try printing tokenString it is empty. The GitHub to this sample is https://github.com/potatogopher/jwt-go-example/blob/master/server.go Why am I getting invalid error?
var privateKey []byte
privateKey, err := ioutil.ReadFile("demo.rsa")
token := jwt.New(jwt.GetSigningMethod("RS256"))
tokenString, err := token.SignedString(privateKey)
fmt.Println("TOKEN:", tokenString)
I think the example code you're referring to uses an outdated API of jwt-go. The RS256 signing method requires the key to be a rsa.PrivateKey and not a byte buffer. This means, that the private key first has to be parsed using the jwt.ParseRSAPrivateKeyFromPEMfunction.
I've updated your example below:
func main() {
tokenString, err := createSignedTokenString()
if err != nil {
panic(err)
}
fmt.Printf("Signed token string:\n%v\n", tokenString)
token, err := parseTokenFromSignedTokenString(tokenString)
if err != nil {
panic(err)
}
fmt.Printf("Parsed token valid = %v, raw token:\n%v\n", token.Valid, token.Raw)
}
func createSignedTokenString() (string, error) {
privateKey, err := ioutil.ReadFile("demo.rsa")
if err != nil {
return "", fmt.Errorf("error reading private key file: %v\n", err)
}
key, err := jwt.ParseRSAPrivateKeyFromPEM(privateKey)
if err != nil {
return "", fmt.Errorf("error parsing RSA private key: %v\n", err)
}
token := jwt.New(jwt.SigningMethodRS256)
tokenString, err := token.SignedString(key)
if err != nil {
return "", fmt.Errorf("error signing token: %v\n", err)
}
return tokenString, nil
}
func parseTokenFromSignedTokenString(tokenString string) (*jwt.Token, error) {
publicKey, err := ioutil.ReadFile("demo.rsa.pub")
if err != nil {
return nil, fmt.Errorf("error reading public key file: %v\n", err)
}
key, err := jwt.ParseRSAPublicKeyFromPEM(publicKey)
if err != nil {
return nil, fmt.Errorf("error parsing RSA public key: %v\n", err)
}
parsedToken, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return key, nil
})
if err != nil {
return nil, fmt.Errorf("error parsing token: %v", err)
}
return parsedToken, nil
}
You need to create the private key with this command: openssl genrsa -out demo.rsa
If you dont want to do that, you can also use the hmac signing method where you only have to supply a secret key/string.
Example:
key := []byte("test")
token := jwt.New(jwt.SigningMethodHS256)
tokenString, err := token.SignedString(key)
fmt.Println("TOKEN:", tokenString)
I'm trying to download a remote file over ssh
The following approach works fine on shell
ssh hostname "tar cz /opt/local/folder" > folder.tar.gz
However the same approach on golang giving some difference in output artifact size. For example the same folders with pure shell produce artifact gz file 179B and same with go script 178B.
I assume that something has been missed from io.Reader or session got closed earlier. Kindly ask you guys to help.
Here is the example of my script:
func executeCmd(cmd, hostname string, config *ssh.ClientConfig, path string) error {
conn, _ := ssh.Dial("tcp", hostname+":22", config)
session, err := conn.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
r, _ := session.StdoutPipe()
scanner := bufio.NewScanner(r)
go func() {
defer session.Close()
name := fmt.Sprintf("%s/backup_folder_%v.tar.gz", path, time.Now().Unix())
file, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
panic(err)
}
defer file.Close()
for scanner.Scan() {
fmt.Println(scanner.Bytes())
if err := scanner.Err(); err != nil {
fmt.Println(err)
}
if _, err = file.Write(scanner.Bytes()); err != nil {
log.Fatal(err)
}
}
}()
if err := session.Run(cmd); err != nil {
fmt.Println(err.Error())
panic("Failed to run: " + err.Error())
}
return nil
}
Thanks!
bufio.Scanner is for newline delimited text. According to the documentation, the scanner will remove the newline characters, stripping any 10s out of your binary file.
You don't need a goroutine to do the copy, because you can use session.Start to start the process asynchronously.
You probably don't need to use bufio either. You should be using io.Copy to copy the file, which has an internal buffer already on top of any buffering already done in the ssh client itself. If an additional buffer is needed for performance, wrap the session output in a bufio.Reader
Finally, you return an error value, so use it rather than panic'ing on regular error conditions.
conn, err := ssh.Dial("tcp", hostname+":22", config)
if err != nil {
return err
}
session, err := conn.NewSession()
if err != nil {
return err
}
defer session.Close()
r, err := session.StdoutPipe()
if err != nil {
return err
}
name := fmt.Sprintf("%s/backup_folder_%v.tar.gz", path, time.Now().Unix())
file, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return err
}
defer file.Close()
if err := session.Start(cmd); err != nil {
return err
}
n, err := io.Copy(file, r)
if err != nil {
return err
}
if err := session.Wait(); err != nil {
return err
}
return nil
You can try doing something like this:
r, _ := session.StdoutPipe()
reader := bufio.NewReader(r)
go func() {
defer session.Close()
// open file etc
// 10 is the number of bytes you'd like to copy in one write operation
p := make([]byte, 10)
for {
n, err := reader.Read(p)
if err == io.EOF {
break
}
if err != nil {
log.Fatal("err", err)
}
if _, err = file.Write(p[:n]); err != nil {
log.Fatal(err)
}
}
}()
Make sure your goroutines are synchronized properly so output is completeky written to the file.