Skip to content

Commit be14649

Browse files
committed
Move decryption to pkg
1 parent 6c51971 commit be14649

File tree

18 files changed

+996
-227
lines changed

18 files changed

+996
-227
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ WORKDIR "/code"
66
ADD . "/code"
77
RUN make BINARY=spring-config-decryptor ${MAKE_TARGET}
88

9-
FROM alpine:3.11
9+
FROM alpine:3.12
1010
COPY --from=builder /code/spring-config-decryptor /spring-config-decryptor
1111
ENTRYPOINT ["/spring-config-decryptor"]

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
.PHONY: clean build fmt test
44

5-
TAG ?= "v0.0.2"
5+
TAG ?= "v0.0.3"
66

77
BUILD_FLAGS ?=
88
BINARY ?= spring-config-decryptor

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/grepplabs/spring-config-decryptor
22

33
go 1.14
44

5-
require golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
5+
require (
6+
github.com/pkg/errors v0.9.1
7+
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
8+
)

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
2+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
13
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
2-
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
3-
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
4-
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
5-
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
4+
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
5+
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
66
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
77
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
88
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

main.go

Lines changed: 4 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -1,227 +1,15 @@
11
package main
22

33
import (
4-
"bufio"
5-
"crypto/aes"
6-
"crypto/cipher"
7-
"crypto/rand"
8-
"crypto/rsa"
9-
"crypto/sha1"
10-
"crypto/x509"
11-
"encoding/base64"
12-
"encoding/binary"
13-
"encoding/hex"
14-
"encoding/pem"
15-
"errors"
164
"flag"
175
"fmt"
186
"io"
197
"io/ioutil"
208
"os"
21-
"regexp"
22-
"strings"
239

24-
"golang.org/x/crypto/pbkdf2"
10+
"github.com/grepplabs/spring-config-decryptor/pkg/decryptor"
2511
)
2612

27-
const (
28-
cipherPrefix = "{cipher}"
29-
defaultSalt = "deadbeef"
30-
)
31-
32-
var (
33-
cipherPattern = regexp.MustCompile(`{cipher}([A-Za-z0-9+/=]*)`)
34-
)
35-
36-
type ValueDecryptorOption func(decryptor *ValueDecryptor) error
37-
38-
type ValueDecryptor struct {
39-
privateKey *rsa.PrivateKey
40-
salt []byte
41-
}
42-
43-
func NewValueDecryptor(key []byte, options ...ValueDecryptorOption) (*ValueDecryptor, error) {
44-
privateKey, err := ParsePrivateKey(key)
45-
if err != nil {
46-
return nil, err
47-
}
48-
result := &ValueDecryptor{privateKey: privateKey}
49-
if err := WithSalt(defaultSalt)(result); err != nil {
50-
return nil, err
51-
}
52-
for _, option := range options {
53-
if err = option(result); err != nil {
54-
return nil, err
55-
}
56-
}
57-
return result, nil
58-
}
59-
60-
func ParsePrivateKey(key []byte) (*rsa.PrivateKey, error) {
61-
block, _ := pem.Decode(key)
62-
if block != nil {
63-
key = block.Bytes
64-
}
65-
parsedKey, err := x509.ParsePKCS8PrivateKey(key)
66-
if err != nil {
67-
parsedKey, err = x509.ParsePKCS1PrivateKey(key)
68-
if err != nil {
69-
return nil, fmt.Errorf("private key should be a PEM or plain PKCS1 or PKCS8; parse error: %v", err)
70-
}
71-
}
72-
parsed, ok := parsedKey.(*rsa.PrivateKey)
73-
if !ok {
74-
return nil, errors.New("private key is invalid")
75-
}
76-
return parsed, nil
77-
}
78-
79-
func WithSalt(salt string) ValueDecryptorOption {
80-
return func(decryptor *ValueDecryptor) error {
81-
if saltBytes, err := hex.DecodeString(salt); err != nil {
82-
return fmt.Errorf("salt '%s' cannot be hex decoded: %v", salt, err)
83-
} else {
84-
decryptor.salt = saltBytes
85-
}
86-
return nil
87-
}
88-
}
89-
90-
func (d ValueDecryptor) DecryptValue(value string) (string, error) {
91-
if !strings.HasPrefix(value, cipherPrefix) {
92-
return value, nil
93-
}
94-
value = strings.TrimPrefix(value, cipherPrefix)
95-
data, err := base64.StdEncoding.DecodeString(value)
96-
if err != nil {
97-
return "", fmt.Errorf("value '%s' cannot be base64 decoded: %v", value, err)
98-
}
99-
return d.decryptData(data)
100-
}
101-
102-
func (d ValueDecryptor) decryptData(data []byte) (string, error) {
103-
if len(data) < 2 {
104-
return "", errors.New("data too short to read session key length")
105-
}
106-
length := binary.BigEndian.Uint16(data[0:2])
107-
108-
if len(data) < int(length+2) {
109-
return "", errors.New("data too short to read session key cipher text")
110-
}
111-
ciphertext := data[2 : length+2]
112-
113-
iv, err := rsa.DecryptPKCS1v15(rand.Reader, d.privateKey, ciphertext)
114-
if err != nil {
115-
return "", err
116-
}
117-
key := pbkdf2.Key([]byte(hex.EncodeToString(iv)), d.salt, 1024, 32, sha1.New)
118-
119-
plaintext, err := d.decryptCBC(key, data[2+length:])
120-
if err != nil {
121-
return "", err
122-
}
123-
return string(d.unpad(plaintext)), nil
124-
}
125-
126-
func (d ValueDecryptor) decryptCBC(key, ciphertext []byte) (plaintext []byte, err error) {
127-
var block cipher.Block
128-
129-
if block, err = aes.NewCipher(key); err != nil {
130-
return
131-
}
132-
if len(ciphertext) < aes.BlockSize {
133-
return nil, errors.New("cipher text length shorter than AES block size")
134-
}
135-
136-
iv := ciphertext[:aes.BlockSize]
137-
ciphertext = ciphertext[aes.BlockSize:]
138-
139-
cbc := cipher.NewCBCDecrypter(block, iv)
140-
cbc.CryptBlocks(ciphertext, ciphertext)
141-
142-
plaintext = ciphertext
143-
144-
return
145-
}
146-
147-
func (d ValueDecryptor) unpad(src []byte) []byte {
148-
length := len(src)
149-
unpadding := int(src[length-1])
150-
return src[:(length - unpadding)]
151-
}
152-
153-
type ConfigDecryptor struct {
154-
valueDecryptor *ValueDecryptor
155-
}
156-
157-
func NewConfigDecryptor(valueDecryptor *ValueDecryptor) *ConfigDecryptor {
158-
return &ConfigDecryptor{
159-
valueDecryptor: valueDecryptor,
160-
}
161-
}
162-
163-
func (c ConfigDecryptor) Decrypt(output io.Writer, input io.Reader) error {
164-
var (
165-
line string
166-
err error
167-
)
168-
rd := bufio.NewReader(input)
169-
wr := bufio.NewWriter(output)
170-
defer func() {
171-
if err := wr.Flush(); err != nil {
172-
exitOnError("writing flush error: %v", err)
173-
}
174-
}()
175-
for {
176-
if line, err = rd.ReadString('\n'); err != nil {
177-
if err == io.EOF {
178-
if err = c.decryptLine(wr, line); err != nil {
179-
return err
180-
}
181-
break
182-
}
183-
return fmt.Errorf("read file line error: %v", err)
184-
}
185-
if err = c.decryptLine(wr, line); err != nil {
186-
return err
187-
}
188-
}
189-
return nil
190-
}
191-
192-
func (c ConfigDecryptor) decryptLine(wr *bufio.Writer, line string) (err error) {
193-
line, err = c.processLine(line)
194-
if err != nil {
195-
return fmt.Errorf("line processing error: %v", err)
196-
}
197-
_, err = wr.WriteString(line)
198-
if err != nil {
199-
return fmt.Errorf("line writing error: %v", err)
200-
}
201-
return nil
202-
}
203-
204-
func (c ConfigDecryptor) processLine(line string) (string, error) {
205-
var sb strings.Builder
206-
207-
for {
208-
ns := cipherPattern.FindStringIndex(line)
209-
if ns == nil {
210-
sb.WriteString(line)
211-
break
212-
}
213-
sb.WriteString(line[:ns[0]])
214-
value := line[ns[0]:ns[1]]
215-
plainText, err := c.valueDecryptor.DecryptValue(value)
216-
if err != nil {
217-
return "", err
218-
}
219-
sb.WriteString(plainText)
220-
line = line[ns[1]:]
221-
}
222-
return sb.String(), nil
223-
}
224-
22513
const (
22614
defaultEnvEncryptKey = "ENCRYPT_KEY"
22715
)
@@ -282,14 +70,12 @@ func main() {
28270
output = f
28371
}
28472

285-
valueDecryptor, err := NewValueDecryptor(key)
73+
dcr, err := decryptor.NewDecryptor(key)
28674
if err != nil {
287-
exitOnError("create value decryptor error: %v", err)
75+
exitOnError("create decryptor error: %v", err)
28876
return
28977
}
290-
291-
configDecryptor := NewConfigDecryptor(valueDecryptor)
292-
err = configDecryptor.Decrypt(output, input)
78+
err = dcr.Decrypt(output, input)
29379
if err != nil {
29480
exitOnError("decrypt error: %v", err)
29581
}

0 commit comments

Comments
 (0)