Skip to content

Commit

Permalink
Merge pull request #19 from pavel-v-chernykh/introduce-key-password
Browse files Browse the repository at this point in the history
Introduce key password
  • Loading branch information
pavlo-v-chernykh authored Oct 26, 2020
2 parents 532a365 + 075577e commit f2a310a
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 8 deletions.
27 changes: 19 additions & 8 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ func (ksd *keyStoreDecoder) readTrustedCertificateEntry(version uint32) (*Truste
return &trustedCertificateEntry, nil
}

func (ksd *keyStoreDecoder) readEntry(version uint32, password []byte) (string, interface{}, error) {
func (ksd *keyStoreDecoder) readEntry(
version uint32, storePassword []byte, keysPasswords ...KeyPassword,
) (string, interface{}, error) {
tag, err := ksd.readUint32()
if err != nil {
return "", nil, fmt.Errorf("read tag: %w", err)
Expand All @@ -216,7 +218,15 @@ func (ksd *keyStoreDecoder) readEntry(version uint32, password []byte) (string,

switch tag {
case privateKeyTag:
entry, err := ksd.readPrivateKeyEntry(version, password)
keyPassword := storePassword

for _, kp := range keysPasswords {
if kp.Alias == alias {
keyPassword = kp.Password
}
}

entry, err := ksd.readPrivateKeyEntry(version, keyPassword)
if err != nil {
return "", nil, fmt.Errorf("read private key entry: %w", err)
}
Expand All @@ -234,18 +244,19 @@ func (ksd *keyStoreDecoder) readEntry(version uint32, password []byte) (string,
}
}

// Decode reads keystore representation from r then decrypts and check signature using password
// Decode reads keystore representation from r then decrypts and check signature using password.
// It is strongly recommended to fill password slice with zero after usage.
func Decode(r io.Reader, password []byte) (KeyStore, error) {
// keysPasswords can be used to decrypt private key entries with passwords other then storePassword.
func Decode(r io.Reader, storePassword []byte, keysPasswords ...KeyPassword) (KeyStore, error) {
ksd := keyStoreDecoder{
r: r,
md: sha1.New(),
}

passwordBytes := passwordBytes(password)
defer zeroing(passwordBytes)
storePasswordBytes := passwordBytes(storePassword)
defer zeroing(storePasswordBytes)

if _, err := ksd.md.Write(passwordBytes); err != nil {
if _, err := ksd.md.Write(storePasswordBytes); err != nil {
return nil, fmt.Errorf("update digest with password: %w", err)
}

Expand Down Expand Up @@ -275,7 +286,7 @@ func Decode(r io.Reader, password []byte) (KeyStore, error) {
keyStore := make(KeyStore, entryNum)

for i := uint32(0); i < entryNum; i++ {
alias, entry, err := ksd.readEntry(version, password)
alias, entry, err := ksd.readEntry(version, storePassword, keysPasswords...)
if err != nil {
return nil, fmt.Errorf("read %d entry: %w", i, err)
}
Expand Down
55 changes: 55 additions & 0 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,3 +534,58 @@ func TestDecode(t *testing.T) {
t.Errorf("unexpected private key")
}
}

func TestDecodeKeyPassword(t *testing.T) {
password := []byte{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}
defer zeroing(password)

keyPassword := []byte{'k', 'e', 'y', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}
defer zeroing(keyPassword)

f, err := os.Open("./testdata/keystore_keypass.jks")
if err != nil {
t.Fatalf("open test data keystore file: %s", err)
}

defer func() {
if err := f.Close(); err != nil {
t.Fatalf("close test data keystore file: %s", err)
}
}()

kp := KeyPassword{Alias: "alias", Password: keyPassword}

keyStore, err := Decode(f, password, kp)
if err != nil {
t.Fatalf("decode test data keystore: %s", err)
}

actualPKE, ok := keyStore["alias"].(*PrivateKeyEntry)
if !ok {
t.Fatalf("assert private key entry")
}

expectedCT, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2020-10-26 12:01:38.387 +0200 EET")
if err != nil {
t.Fatalf("parse creation time: %s", err)
}

if !actualPKE.CreationTime.Equal(expectedCT) {
t.Errorf("unexpected private key entry creation time: '%v' '%v'", actualPKE.CreationTime, expectedCT)
}

if len(actualPKE.CertificateChain) != 1 {
t.Errorf("unexpected private key entry certificate chain length: '%d' '%d'", len(actualPKE.CertificateChain), 0)
}

pkPEM, err := ioutil.ReadFile("./testdata/privkey_keypass.pem")
if err != nil {
t.Fatalf("read expected private key file: %s", err)
}

decodedPK, _ := pem.Decode(pkPEM)

if !reflect.DeepEqual(actualPKE.PrivateKey, decodedPK.Bytes) {
t.Errorf("unexpected private key %v \n %v", actualPKE.PrivateKey, decodedPK.Bytes)
}
}
7 changes: 7 additions & 0 deletions examples/keypass/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/pavel-v-chernykh/keystore-go/v3/examples/keypass

go 1.14

require github.com/pavel-v-chernykh/keystore-go/v3 v3.0.0

replace github.com/pavel-v-chernykh/keystore-go/v3 v3.0.0 => ../..
Binary file added examples/keypass/keystore.jks
Binary file not shown.
54 changes: 54 additions & 0 deletions examples/keypass/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"crypto/x509"
"log"
"os"

"github.com/pavel-v-chernykh/keystore-go/v3"
)

func readKeyStore(filename string, storePassword []byte, keysPasswords ...keystore.KeyPassword) keystore.KeyStore {
f, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatal(err)
}
}()
keyStore, err := keystore.Decode(f, storePassword, keysPasswords...)
if err != nil {
log.Fatal(err)
}
return keyStore
}

func zeroing(s []byte) {
for i := 0; i < len(s); i++ {
s[i] = 0
}
}

// keytool -genkeypair -alias alias -storepass password -keypass keypassword -keyalg RSA -keystore keystore.jks
func main() {
password := []byte{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}
defer zeroing(password)

keyPassword := []byte{'k', 'e', 'y', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}
defer zeroing(password)

kp := keystore.KeyPassword{Alias: "alias", Password: keyPassword}
ks := readKeyStore("keystore.jks", password, kp)

entry := ks["alias"]
privKeyEntry := entry.(*keystore.PrivateKeyEntry)

key, err := x509.ParsePKCS8PrivateKey(privKeyEntry.PrivateKey)
if err != nil {
log.Fatal(err)
}

log.Printf("%#v", key)
}
28 changes: 28 additions & 0 deletions examples/keypass/privkey.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCTjd1HLf1DjOhC
zQiXSHHRXg16qJpqavOU86S5vtgBuLS8a8b9aOEcBQ0H5Lj9GAm3zhKRB2zJ3Aab
iQTCoYq7qbafGmGW7sBNuf0/lCjvnk4wWHsPSqD9SbaQMz6VDCBoCAhykiweAlsu
ap4Niizl5QOVHkVfShUTObGs2T2FBkW249goxbQK+bpxWbXULktAY4qX8ENjU5oK
fi60ZrmeyO0wh7eChqQXUaHthXI0PGbJzqEvC2JB6J0/asZTr6nBgOKvkQgfNbGR
Q1e5xAu8pjcvNw5Pw5VuEfxtKpzA57BpMkv7mTYZfYguSQld+RnB71PIJnSLMcT8
GyaAJgOJAgMBAAECggEBAIcR3fVCjAxB+Hrt5WdW0ZmuVvhpTrELpl47ihbeNC2f
PxMcNdPTJf3YQc1v+kb0sjCnWyeXfGZsG0BVdR4VckHHYXzTUXIwq6h7EPAaWD7A
0yI+XKfq0OQLjisCPc+Cgke40sLOO8Rcs8sRng0W53Kq6VSfmTFZR2GJ9rMbSc1y
QfgTEuVDKKYAItwMZ6kcvlOyYxe4KHqOvl7iz8bi+csfjYNl8U15HfOe0Xc+jNqH
Xil9pLViVxyW5KuXSSKor9fihyHLKqyc25y50kl8YVrUEZINiV2/VCk39aSzACsl
YHvpLqlvhy0Ic1QefM6GCc9vrfTdLhkmzN9dcBTpH2ECgYEA24AY3+ZKrjb52bD8
x7WW2RH3GrCg33QfIwHjdmXtAYTLcNGfSNIiBDKZwBQeGumVpiUWBPTMPuple9Qy
ZD1GbRhMHjVi5IHS6EbNFcis6uOVwrIUZe4yr/5wkMcdctcBwWozu09R6jx7qX6F
BkPkpRaTlpfyJ3I5CaHWJcZysPsCgYEArBcXZE3+SrajOVsCwZSE6VrVfAwzBVxh
l19PPy2WxIBupnM9R3M1KIb0jsQRQwsRkSdrOjRpwdCBYlanAP24u8D9d+M2cx90
BmQTiPf1LuHHynS9xATPdnxP8C+okp9Z/KVOm4SD/asH8eL/tx+ql71gdg9IMN9k
7Fkq0kmwXksCgYEAxL3+jcGnMpxIOp1vFbpn6U03BY9HwsIcno40yYz2cKQMjaT2
dpkSsFyCfy4JBepUnkUjhXG+GK6UI/fA6yQZGfPrVjBX4aWT22qKrHngs/El7Qsd
SXfFqwYO81XlyWd6RVjh4YRpUeDAnt2ucj0sleS0iQjMnhq9kSqj/dvwNT0CgYEA
garAiGawFdG5+UyXLg9feolj1J1IkGCEdE9vYdZTBOsU9LxeTwkC4++UNEKOEysy
6GGATRITIeOeqd9K5NdIq8gF9KCydVoczXf4tDrJdrLPHMCMADfzrn6KqRrv9NK3
NkPEkC/wWGHXka4OKghlPQKHvU1UgubAn8msF1lI2DMCgYEAnWbR5R/EGrmXHQn+
5sOgVEQqp3YKE+d9n4CgQaTAh4mcb2IJArt6r2+8Ez1G8WZvkMPubuE6jvzjBHWb
1L+bIfsU3MsZ4xAaw3AKGzrOWIorlZIINFmuSetTP0Ee5S8ApSrzX/PFUQtJrg5z
d22QeEAmL/nu/PvNC9KiwP9f0wY=
-----END PRIVATE KEY-----
5 changes: 5 additions & 0 deletions keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ type TrustedCertificateEntry struct {
Entry
Certificate Certificate
}

type KeyPassword struct {
Alias string
Password []byte
}
Binary file added testdata/keystore_keypass.jks
Binary file not shown.
28 changes: 28 additions & 0 deletions testdata/privkey_keypass.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCTjd1HLf1DjOhC
zQiXSHHRXg16qJpqavOU86S5vtgBuLS8a8b9aOEcBQ0H5Lj9GAm3zhKRB2zJ3Aab
iQTCoYq7qbafGmGW7sBNuf0/lCjvnk4wWHsPSqD9SbaQMz6VDCBoCAhykiweAlsu
ap4Niizl5QOVHkVfShUTObGs2T2FBkW249goxbQK+bpxWbXULktAY4qX8ENjU5oK
fi60ZrmeyO0wh7eChqQXUaHthXI0PGbJzqEvC2JB6J0/asZTr6nBgOKvkQgfNbGR
Q1e5xAu8pjcvNw5Pw5VuEfxtKpzA57BpMkv7mTYZfYguSQld+RnB71PIJnSLMcT8
GyaAJgOJAgMBAAECggEBAIcR3fVCjAxB+Hrt5WdW0ZmuVvhpTrELpl47ihbeNC2f
PxMcNdPTJf3YQc1v+kb0sjCnWyeXfGZsG0BVdR4VckHHYXzTUXIwq6h7EPAaWD7A
0yI+XKfq0OQLjisCPc+Cgke40sLOO8Rcs8sRng0W53Kq6VSfmTFZR2GJ9rMbSc1y
QfgTEuVDKKYAItwMZ6kcvlOyYxe4KHqOvl7iz8bi+csfjYNl8U15HfOe0Xc+jNqH
Xil9pLViVxyW5KuXSSKor9fihyHLKqyc25y50kl8YVrUEZINiV2/VCk39aSzACsl
YHvpLqlvhy0Ic1QefM6GCc9vrfTdLhkmzN9dcBTpH2ECgYEA24AY3+ZKrjb52bD8
x7WW2RH3GrCg33QfIwHjdmXtAYTLcNGfSNIiBDKZwBQeGumVpiUWBPTMPuple9Qy
ZD1GbRhMHjVi5IHS6EbNFcis6uOVwrIUZe4yr/5wkMcdctcBwWozu09R6jx7qX6F
BkPkpRaTlpfyJ3I5CaHWJcZysPsCgYEArBcXZE3+SrajOVsCwZSE6VrVfAwzBVxh
l19PPy2WxIBupnM9R3M1KIb0jsQRQwsRkSdrOjRpwdCBYlanAP24u8D9d+M2cx90
BmQTiPf1LuHHynS9xATPdnxP8C+okp9Z/KVOm4SD/asH8eL/tx+ql71gdg9IMN9k
7Fkq0kmwXksCgYEAxL3+jcGnMpxIOp1vFbpn6U03BY9HwsIcno40yYz2cKQMjaT2
dpkSsFyCfy4JBepUnkUjhXG+GK6UI/fA6yQZGfPrVjBX4aWT22qKrHngs/El7Qsd
SXfFqwYO81XlyWd6RVjh4YRpUeDAnt2ucj0sleS0iQjMnhq9kSqj/dvwNT0CgYEA
garAiGawFdG5+UyXLg9feolj1J1IkGCEdE9vYdZTBOsU9LxeTwkC4++UNEKOEysy
6GGATRITIeOeqd9K5NdIq8gF9KCydVoczXf4tDrJdrLPHMCMADfzrn6KqRrv9NK3
NkPEkC/wWGHXka4OKghlPQKHvU1UgubAn8msF1lI2DMCgYEAnWbR5R/EGrmXHQn+
5sOgVEQqp3YKE+d9n4CgQaTAh4mcb2IJArt6r2+8Ez1G8WZvkMPubuE6jvzjBHWb
1L+bIfsU3MsZ4xAaw3AKGzrOWIorlZIINFmuSetTP0Ee5S8ApSrzX/PFUQtJrg5z
d22QeEAmL/nu/PvNC9KiwP9f0wY=
-----END PRIVATE KEY-----

0 comments on commit f2a310a

Please sign in to comment.