Skip to content

Commit 6a52777

Browse files
authored
support both pem and pk12 jks binary key format (#19)
* support both pem and pk12 jks binary key format * fix the key file name in test
1 parent ebe74c7 commit 6a52777

File tree

4 files changed

+98
-30
lines changed

4 files changed

+98
-30
lines changed

src/icrypto/pulsar-jwt.go

+90-28
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import (
66
"bufio"
77
"crypto/rsa"
88
"crypto/x509"
9+
"encoding/binary"
910
"encoding/pem"
1011
"errors"
12+
"io/ioutil"
1113
"log"
1214
"os"
1315
"time"
@@ -117,66 +119,126 @@ func (keys *RSAKeyPair) GetTokenRemainingValidity(timestamp interface{}) int {
117119
return expireOffset
118120
}
119121

120-
func getPrivateKey(privateKeyPath string) *rsa.PrivateKey {
121-
privateKeyFile, err := os.Open(privateKeyPath)
122+
// supports pk12 jks binary format
123+
func readPK12(file string) ([]byte, error) {
124+
osFile, err := os.Open(file)
122125
if err != nil {
123-
panic(err)
126+
return nil, err
124127
}
128+
reader := bufio.NewReaderSize(osFile, 4)
125129

126-
pemfileinfo, _ := privateKeyFile.Stat()
127-
var size int64 = pemfileinfo.Size()
128-
pembytes := make([]byte, size)
130+
return ioutil.ReadAll(reader)
131+
}
129132

130-
buffer := bufio.NewReader(privateKeyFile)
133+
// decode PEM format to array of bytes
134+
func decodePEM(pemFilePath string) ([]byte, error) {
135+
keyFile, err := os.Open(pemFilePath)
136+
defer keyFile.Close()
137+
if err != nil {
138+
return nil, err
139+
}
140+
141+
pemfileinfo, _ := keyFile.Stat()
142+
pembytes := make([]byte, pemfileinfo.Size())
143+
144+
buffer := bufio.NewReader(keyFile)
131145
_, err = buffer.Read(pembytes)
132146

133147
data, _ := pem.Decode([]byte(pembytes))
148+
return data.Bytes, err
149+
}
134150

135-
privateKeyFile.Close()
136-
137-
// PKCS8 comply with Pulsar Java generated private key
138-
key, err := x509.ParsePKCS8PrivateKey(data.Bytes)
151+
func parseX509PKCS8PrivateKey(data []byte) *rsa.PrivateKey {
152+
key, err := x509.ParsePKCS8PrivateKey(data)
139153

140154
if err != nil {
141155
panic(err)
142156
}
143157

144-
privateKeyImported, ok := key.(*rsa.PrivateKey)
158+
rsaPrivate, ok := key.(*rsa.PrivateKey)
145159
if !ok {
146160
log.Fatalf("expected key to be of type *ecdsa.PrivateKey, but actual was %T", key)
147161
}
148162

149-
return privateKeyImported
163+
return rsaPrivate
150164
}
151165

152-
func getPublicKey(publicKeyPath string) *rsa.PublicKey {
153-
publicKeyFile, err := os.Open(publicKeyPath)
166+
func parseX509PKIXPublicKey(data []byte) *rsa.PublicKey {
167+
publicKeyImported, err := x509.ParsePKIXPublicKey(data)
168+
154169
if err != nil {
155170
panic(err)
156171
}
157172

158-
pemfileinfo, _ := publicKeyFile.Stat()
159-
var size int64 = pemfileinfo.Size()
160-
pembytes := make([]byte, size)
173+
rsaPub, ok := publicKeyImported.(*rsa.PublicKey)
174+
if !ok {
175+
panic(err)
176+
}
161177

162-
buffer := bufio.NewReader(publicKeyFile)
163-
_, err = buffer.Read(pembytes)
178+
return rsaPub
179+
}
164180

165-
data, _ := pem.Decode([]byte(pembytes))
181+
// Since we support PEM And binary fomat of PKCS12/X509 keys,
182+
// this function tries to determine which format
183+
func fileFormat(file string) (string, error) {
184+
osFile, err := os.Open(file)
185+
if err != nil {
186+
return "", err
187+
}
188+
reader := bufio.NewReaderSize(osFile, 4)
189+
// attempt to guess based on first 4 bytes of input
190+
data, err := reader.Peek(4)
191+
if err != nil {
192+
return "", err
193+
}
166194

167-
publicKeyFile.Close()
195+
magic := binary.BigEndian.Uint32(data)
196+
if magic == 0x2D2D2D2D || magic == 0x434f4e4e {
197+
// Starts with '----' or 'CONN' (what s_client prints...)
198+
return "PEM", nil
199+
}
200+
if magic&0xFFFF0000 == 0x30820000 {
201+
// Looks like the input is DER-encoded, so it's either PKCS12 or X.509.
202+
if magic&0x0000FF00 == 0x0300 {
203+
// Probably X.509
204+
return "DER", nil
205+
}
206+
return "PKCS12", nil
207+
}
168208

169-
publicKeyImported, err := x509.ParsePKIXPublicKey(data.Bytes)
209+
return "", errors.New("undermined format")
210+
}
170211

212+
func getDataFromKeyFile(file string) ([]byte, error) {
213+
format, err := fileFormat(file)
171214
if err != nil {
172-
panic(err)
215+
return nil, err
173216
}
174217

175-
rsaPub, ok := publicKeyImported.(*rsa.PublicKey)
218+
switch format {
219+
case "PEM":
220+
return decodePEM(file)
221+
case "PKCS12":
222+
return readPK12(file)
223+
default:
224+
return nil, errors.New("unsupported format")
225+
}
226+
}
176227

177-
if !ok {
178-
panic(err)
228+
func getPrivateKey(file string) *rsa.PrivateKey {
229+
data, err := getDataFromKeyFile(file)
230+
if err != nil {
231+
log.Fatal(err)
179232
}
180233

181-
return rsaPub
234+
return parseX509PKCS8PrivateKey(data)
235+
}
236+
237+
func getPublicKey(file string) *rsa.PublicKey {
238+
data, err := getDataFromKeyFile(file)
239+
if err != nil {
240+
log.Fatal(err)
241+
}
242+
243+
return parseX509PKIXPublicKey(data)
182244
}

src/unit-test/crypto_test.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,14 @@ func TestGenWriteKey(t *testing.T) {
8686
}
8787

8888
func TestJWTRSASignAndVerify(t *testing.T) {
89-
publicKeyPath := "./example_public_key.pub"
90-
privateKeyPath := "./example_private_key"
89+
// PK12 binary format
90+
testTokenSignAndVerify(t, "./pk12-binary-private.key", "./pk12-binary-public.key")
91+
92+
// PEM format
93+
testTokenSignAndVerify(t, "./example_private_key", "./example_public_key.pub")
94+
}
95+
96+
func testTokenSignAndVerify(t *testing.T, privateKeyPath, publicKeyPath string) {
9197
authen := NewRSAKeyPair(privateKeyPath, publicKeyPath)
9298

9399
tokenString, err := authen.GenerateToken("myadmin")

src/unit-test/pk12-binary-private.key

1.19 KB
Binary file not shown.

src/unit-test/pk12-binary-public.key

294 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)