-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New RSA encrypt/decrypt functions, and new base64.DecodeBytes function
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
- Loading branch information
Dave Henderson
committed
Jun 15, 2020
1 parent
bc9fdd0
commit ee3069a
Showing
13 changed files
with
826 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| package crypto | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "crypto/rand" | ||
| "crypto/rsa" | ||
| "crypto/x509" | ||
| "encoding/pem" | ||
| "fmt" | ||
| "strings" | ||
| ) | ||
|
|
||
| // RSAEncrypt - use the given public key to encrypt the given plaintext. The key | ||
| // should be a PEM-encoded RSA public key in PKIX, ASN.1 DER form, typically | ||
| // beginning with "PUBLIC KEY". PKCS#1 format is also supported as a fallback. | ||
| // The output will not be encoded, so consider base64-encoding it for display. | ||
| func RSAEncrypt(key string, in []byte) ([]byte, error) { | ||
| block, _ := pem.Decode([]byte(key)) | ||
| if block == nil { | ||
| return nil, fmt.Errorf("failed to read key %q: no key found", key) | ||
| } | ||
|
|
||
| pub, err := x509.ParsePKIXPublicKey(block.Bytes) | ||
| if err != nil { | ||
| if strings.Contains(err.Error(), "use ParsePKCS1PublicKey instead") { | ||
| pub, err = x509.ParsePKCS1PublicKey(block.Bytes) | ||
| } | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to parse public key: %w", err) | ||
| } | ||
| } | ||
| pubKey, ok := pub.(*rsa.PublicKey) | ||
| if !ok { | ||
| return nil, fmt.Errorf("public key in wrong format, was %T", pub) | ||
| } | ||
|
|
||
| out, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, in) | ||
| return out, err | ||
| } | ||
|
|
||
| // RSADecrypt - decrypt the ciphertext with the given private key. The key | ||
| // must be a PEM-encoded RSA private key in PKCS#1, ASN.1 DER form, typically | ||
| // beginning with "RSA PRIVATE KEY". The input text must be plain ciphertext, | ||
| // not base64-encoded. | ||
| func RSADecrypt(key string, in []byte) ([]byte, error) { | ||
| block, _ := pem.Decode([]byte(key)) | ||
| if block == nil { | ||
| return nil, fmt.Errorf("failed to read key %q: no key found", key) | ||
| } | ||
|
|
||
| priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("invalid private key: %w", err) | ||
| } | ||
|
|
||
| out, err := priv.Decrypt(nil, in, nil) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to decrypt: %w", err) | ||
| } | ||
| return out, nil | ||
| } | ||
|
|
||
| // RSAGenerateKey - | ||
| func RSAGenerateKey(bits int) ([]byte, error) { | ||
| priv, err := rsa.GenerateKey(rand.Reader, bits) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to generate RSA private key: %w", err) | ||
| } | ||
| block := &pem.Block{ | ||
| Type: "RSA PRIVATE KEY", | ||
| Bytes: x509.MarshalPKCS1PrivateKey(priv), | ||
| } | ||
| buf := &bytes.Buffer{} | ||
| err = pem.Encode(buf, block) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to encode generated RSA private key: pem encoding failed: %w", err) | ||
| } | ||
| return buf.Bytes(), nil | ||
| } | ||
|
|
||
| // RSADerivePublicKey - | ||
| func RSADerivePublicKey(privateKey []byte) ([]byte, error) { | ||
| block, _ := pem.Decode(privateKey) | ||
| if block == nil { | ||
| return nil, fmt.Errorf("failed to read key: no key found") | ||
| } | ||
|
|
||
| priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("invalid private key: %w", err) | ||
| } | ||
|
|
||
| b, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to marshal PKIX public key: %w", err) | ||
| } | ||
|
|
||
| block = &pem.Block{ | ||
| Type: "PUBLIC KEY", | ||
| Bytes: b, | ||
| } | ||
|
|
||
| return pem.EncodeToMemory(block), nil | ||
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| package crypto | ||
|
|
||
| import ( | ||
| "crypto/rand" | ||
| "crypto/rsa" | ||
| "crypto/x509" | ||
| "encoding/pem" | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func genPKCS1PrivKey() (*rsa.PrivateKey, string) { | ||
| rsaPriv, _ := rsa.GenerateKey(rand.Reader, 4096) | ||
| privBlock := &pem.Block{ | ||
| Type: "RSA PRIVATE KEY", | ||
| Bytes: x509.MarshalPKCS1PrivateKey(rsaPriv), | ||
| } | ||
| return rsaPriv, string(pem.EncodeToMemory(privBlock)) | ||
| } | ||
|
|
||
| func derivePKIXPrivKey(priv *rsa.PrivateKey) string { | ||
| privBlock := &pem.Block{ | ||
| Type: "RSA PRIVATE KEY", | ||
| Bytes: x509.MarshalPKCS1PrivateKey(priv), | ||
| } | ||
| return string(pem.EncodeToMemory(privBlock)) | ||
| } | ||
|
|
||
| func derivePKIXPubKey(priv *rsa.PrivateKey) string { | ||
| b, _ := x509.MarshalPKIXPublicKey(&priv.PublicKey) | ||
| pubBlock := &pem.Block{ | ||
| Type: "PUBLIC KEY", | ||
| Bytes: b, | ||
| } | ||
| testPubKey := string(pem.EncodeToMemory(pubBlock)) | ||
| return testPubKey | ||
| } | ||
|
|
||
| func derivePKCS1PubKey(priv *rsa.PrivateKey) string { | ||
| b := x509.MarshalPKCS1PublicKey(&priv.PublicKey) | ||
| pubBlock := &pem.Block{ | ||
| Type: "RSA PUBLIC KEY", | ||
| Bytes: b, | ||
| } | ||
| testPubKey := string(pem.EncodeToMemory(pubBlock)) | ||
| return testPubKey | ||
| } | ||
|
|
||
| func TestRSACrypt(t *testing.T) { | ||
| priv, testPrivKey := genPKCS1PrivKey() | ||
| testPubKey := derivePKIXPubKey(priv) | ||
|
|
||
| in := []byte("hello world") | ||
| key := "bad key" | ||
| _, err := RSAEncrypt(key, in) | ||
| assert.Error(t, err) | ||
|
|
||
| _, err = RSADecrypt(key, in) | ||
| assert.Error(t, err) | ||
|
|
||
| key = "" | ||
| _, err = RSAEncrypt(key, in) | ||
| assert.Error(t, err) | ||
| _, err = RSADecrypt(key, in) | ||
| assert.Error(t, err) | ||
|
|
||
| enc, err := RSAEncrypt(testPubKey, in) | ||
| assert.NoError(t, err) | ||
| dec, err := RSADecrypt(testPrivKey, enc) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, in, dec) | ||
|
|
||
| testPubKey = derivePKCS1PubKey(priv) | ||
| enc, err = RSAEncrypt(testPubKey, in) | ||
| assert.NoError(t, err) | ||
| dec, err = RSADecrypt(testPrivKey, enc) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, in, dec) | ||
| } | ||
|
|
||
| func TestRSAGenerateKey(t *testing.T) { | ||
| _, err := RSAGenerateKey(0) | ||
| assert.Error(t, err) | ||
|
|
||
| key, err := RSAGenerateKey(12) | ||
| assert.NoError(t, err) | ||
| assert.True(t, strings.HasPrefix(string(key), | ||
| "-----BEGIN RSA PRIVATE KEY-----")) | ||
| assert.True(t, strings.HasSuffix(string(key), | ||
| "-----END RSA PRIVATE KEY-----\n")) | ||
| } | ||
|
|
||
| func TestRSADerivePublicKey(t *testing.T) { | ||
| _, err := RSADerivePublicKey(nil) | ||
| assert.Error(t, err) | ||
|
|
||
| _, err = RSADerivePublicKey([]byte(`-----BEGIN FOO----- | ||
| -----END FOO-----`)) | ||
| assert.Error(t, err) | ||
|
|
||
| priv, privKey := genPKCS1PrivKey() | ||
| expected := derivePKIXPubKey(priv) | ||
|
|
||
| actual, err := RSADerivePublicKey([]byte(privKey)) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, expected, string(actual)) | ||
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.