Skip to content

Commit

Permalink
Support encrypted private keys (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
nakabonne authored Dec 28, 2020
1 parent 31b8bab commit 8f4ca3a
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 20 deletions.
2 changes: 1 addition & 1 deletion commands/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (r *copyRunner) encrypt(plaintext []byte, saltFunc func() ([]byte, error))
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", r.publicKeyFile, err)
}
encryptedSessKey, err := pbcrypto.EncryptWithRSA(pubKey, sessionKey)
encryptedSessKey, err := pbcrypto.EncryptWithRSA(sessionKey, pubKey)
if err != nil {
return nil, fmt.Errorf("failed to encrypt the session key: %w", err)
}
Expand Down
24 changes: 17 additions & 7 deletions commands/paste.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package commands

import (
"bytes"
"encoding/json"
"errors"
"fmt"
Expand All @@ -17,12 +18,13 @@ import (
)

type pasteRunner struct {
timeout time.Duration
password string
symmetricKeyFile string
privateKeyFile string
basicAuth string
maxBufSize string
timeout time.Duration
password string
symmetricKeyFile string
privateKeyFile string
privateKeyPasswordFile string
basicAuth string
maxBufSize string

stdout io.Writer
stderr io.Writer
Expand All @@ -44,6 +46,7 @@ func NewPasteCommand(stdout, stderr io.Writer) *cobra.Command {
cmd.Flags().StringVarP(&r.password, "password", "p", "", "Password to derive the symmetric-key to be used for decryption")
cmd.Flags().StringVarP(&r.symmetricKeyFile, "symmetric-key-file", "k", "", "Path to symmetric-key file to be used for decryption")
cmd.Flags().StringVarP(&r.privateKeyFile, "private-key-file", "K", "", "Path to an RSA private-key file to be used for decryption; Must be in PEM or DER format")
cmd.Flags().StringVar(&r.privateKeyPasswordFile, "private-key-password-file", "", "Path to password file to decrypt the encrypted private key")
cmd.Flags().StringVarP(&r.basicAuth, "basic-auth", "a", "", "Basic authentication, username:password")
cmd.Flags().StringVar(&r.maxBufSize, "max-size", "500mb", "Max data size with unit")
return cmd
Expand Down Expand Up @@ -107,7 +110,14 @@ func (r *pasteRunner) decrypt(data []byte, saltFunc func() ([]byte, error)) ([]b
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", r.privateKeyFile, err)
}
sessKey, err := pbcrypto.DecryptWithRSA(privKey, withSessKey.EncryptedSessionKey)
var keyPassword []byte
if r.privateKeyPasswordFile != "" {
keyPassword, err = ioutil.ReadFile(r.privateKeyPasswordFile)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", r.privateKeyPasswordFile, err)
}
}
sessKey, err := pbcrypto.DecryptWithRSA(withSessKey.EncryptedSessionKey, privKey, bytes.TrimSpace(keyPassword))
if err != nil {
return nil, fmt.Errorf("failed to decrypt the session key: %w", err)
}
Expand Down
14 changes: 11 additions & 3 deletions crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func Decrypt(key, encrypted []byte) ([]byte, error) {

// EncryptWithRSA encrypts the given data with RSA-OAEP.
// pubKey must be a RSA public key in PEM or DER format.
func EncryptWithRSA(pubKey, plaintext []byte) ([]byte, error) {
func EncryptWithRSA(plaintext, pubKey []byte) ([]byte, error) {
// At first it assumes the pubKey is in DER format.
derKey, err := parsePubKeyInDER(pubKey)
if err == nil {
Expand Down Expand Up @@ -106,7 +106,7 @@ func parsePubKeyInDER(der []byte) (*rsa.PublicKey, error) {

// DecryptWithRSA decrypts the given encrypted data with RSA-OAEP.
// privKey must be a RSA private key in PEM or DER format.
func DecryptWithRSA(privKey, encrypted []byte) ([]byte, error) {
func DecryptWithRSA(encrypted, privKey, password []byte) ([]byte, error) {
// At first it assumes the privKey is in DER format.
derKey, err := parsePrivKeyInDER(privKey)
if err == nil {
Expand All @@ -118,7 +118,15 @@ func DecryptWithRSA(privKey, encrypted []byte) ([]byte, error) {
if pem == nil {
return nil, fmt.Errorf("given private key format is neither DER nor PEM")
}
pemKey, err := parsePrivKeyInDER(pem.Bytes)
bytes := pem.Bytes
if x509.IsEncryptedPEMBlock(pem) {
var err error
bytes, err = x509.DecryptPEMBlock(pem, password)
if err != nil {
return nil, fmt.Errorf("failed to decrypt the encrypted private key: %w", err)
}
}
pemKey, err := parsePrivKeyInDER(bytes)
if err != nil {
return nil, err
}
Expand Down
19 changes: 10 additions & 9 deletions crypto/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,20 @@ YQIDAQAB
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := EncryptWithRSA([]byte(tt.pubKey), []byte(tt.data))
_, err := EncryptWithRSA([]byte(tt.data), []byte(tt.pubKey))
assert.Equal(t, tt.wantErr, err != nil)
})
}
}

func TestDecryptWithRSA(t *testing.T) {
tests := []struct {
name string
privKey string
pubKey string
data string
wantErr bool
name string
privKey string
pubKey string
data string
password string
wantErr bool
}{
{
name: "private key is not in pem format",
Expand All @@ -134,7 +135,7 @@ vI7VebGGA3OUmbqWMPoIR2epT3KLRi2pEA==
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := DecryptWithRSA([]byte(tt.privKey), []byte(tt.data))
_, err := DecryptWithRSA([]byte(tt.data), []byte(tt.privKey), []byte(tt.password))
assert.Equal(t, tt.wantErr, err != nil)
})
}
Expand Down Expand Up @@ -280,9 +281,9 @@ QwIDAQAB
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
encrypted, err := EncryptWithRSA([]byte(tt.pubKey), []byte(tt.data))
encrypted, err := EncryptWithRSA([]byte(tt.data), []byte(tt.pubKey))
assert.NoError(t, err)
_, err = DecryptWithRSA([]byte(tt.privKey), encrypted)
_, err = DecryptWithRSA(encrypted, []byte(tt.privKey), nil)
assert.Equal(t, tt.wantErr, err != nil)
})
}
Expand Down

0 comments on commit 8f4ca3a

Please # to comment.