From 6a52777bab6789f42f88d18c32ac0bd52f26d7fc Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 7 Jan 2020 07:31:23 -0500 Subject: [PATCH] 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 --- src/icrypto/pulsar-jwt.go | 118 ++++++++++++++++++++------ src/unit-test/crypto_test.go | 10 ++- src/unit-test/pk12-binary-private.key | Bin 0 -> 1219 bytes src/unit-test/pk12-binary-public.key | Bin 0 -> 294 bytes 4 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 src/unit-test/pk12-binary-private.key create mode 100644 src/unit-test/pk12-binary-public.key diff --git a/src/icrypto/pulsar-jwt.go b/src/icrypto/pulsar-jwt.go index a645862..994f875 100644 --- a/src/icrypto/pulsar-jwt.go +++ b/src/icrypto/pulsar-jwt.go @@ -6,8 +6,10 @@ import ( "bufio" "crypto/rsa" "crypto/x509" + "encoding/binary" "encoding/pem" "errors" + "io/ioutil" "log" "os" "time" @@ -117,66 +119,126 @@ func (keys *RSAKeyPair) GetTokenRemainingValidity(timestamp interface{}) int { return expireOffset } -func getPrivateKey(privateKeyPath string) *rsa.PrivateKey { - privateKeyFile, err := os.Open(privateKeyPath) +// supports pk12 jks binary format +func readPK12(file string) ([]byte, error) { + osFile, err := os.Open(file) if err != nil { - panic(err) + return nil, err } + reader := bufio.NewReaderSize(osFile, 4) - pemfileinfo, _ := privateKeyFile.Stat() - var size int64 = pemfileinfo.Size() - pembytes := make([]byte, size) + return ioutil.ReadAll(reader) +} - buffer := bufio.NewReader(privateKeyFile) +// decode PEM format to array of bytes +func decodePEM(pemFilePath string) ([]byte, error) { + keyFile, err := os.Open(pemFilePath) + defer keyFile.Close() + if err != nil { + return nil, err + } + + pemfileinfo, _ := keyFile.Stat() + pembytes := make([]byte, pemfileinfo.Size()) + + buffer := bufio.NewReader(keyFile) _, err = buffer.Read(pembytes) data, _ := pem.Decode([]byte(pembytes)) + return data.Bytes, err +} - privateKeyFile.Close() - - // PKCS8 comply with Pulsar Java generated private key - key, err := x509.ParsePKCS8PrivateKey(data.Bytes) +func parseX509PKCS8PrivateKey(data []byte) *rsa.PrivateKey { + key, err := x509.ParsePKCS8PrivateKey(data) if err != nil { panic(err) } - privateKeyImported, ok := key.(*rsa.PrivateKey) + rsaPrivate, ok := key.(*rsa.PrivateKey) if !ok { log.Fatalf("expected key to be of type *ecdsa.PrivateKey, but actual was %T", key) } - return privateKeyImported + return rsaPrivate } -func getPublicKey(publicKeyPath string) *rsa.PublicKey { - publicKeyFile, err := os.Open(publicKeyPath) +func parseX509PKIXPublicKey(data []byte) *rsa.PublicKey { + publicKeyImported, err := x509.ParsePKIXPublicKey(data) + if err != nil { panic(err) } - pemfileinfo, _ := publicKeyFile.Stat() - var size int64 = pemfileinfo.Size() - pembytes := make([]byte, size) + rsaPub, ok := publicKeyImported.(*rsa.PublicKey) + if !ok { + panic(err) + } - buffer := bufio.NewReader(publicKeyFile) - _, err = buffer.Read(pembytes) + return rsaPub +} - data, _ := pem.Decode([]byte(pembytes)) +// Since we support PEM And binary fomat of PKCS12/X509 keys, +// this function tries to determine which format +func fileFormat(file string) (string, error) { + osFile, err := os.Open(file) + if err != nil { + return "", err + } + reader := bufio.NewReaderSize(osFile, 4) + // attempt to guess based on first 4 bytes of input + data, err := reader.Peek(4) + if err != nil { + return "", err + } - publicKeyFile.Close() + magic := binary.BigEndian.Uint32(data) + if magic == 0x2D2D2D2D || magic == 0x434f4e4e { + // Starts with '----' or 'CONN' (what s_client prints...) + return "PEM", nil + } + if magic&0xFFFF0000 == 0x30820000 { + // Looks like the input is DER-encoded, so it's either PKCS12 or X.509. + if magic&0x0000FF00 == 0x0300 { + // Probably X.509 + return "DER", nil + } + return "PKCS12", nil + } - publicKeyImported, err := x509.ParsePKIXPublicKey(data.Bytes) + return "", errors.New("undermined format") +} +func getDataFromKeyFile(file string) ([]byte, error) { + format, err := fileFormat(file) if err != nil { - panic(err) + return nil, err } - rsaPub, ok := publicKeyImported.(*rsa.PublicKey) + switch format { + case "PEM": + return decodePEM(file) + case "PKCS12": + return readPK12(file) + default: + return nil, errors.New("unsupported format") + } +} - if !ok { - panic(err) +func getPrivateKey(file string) *rsa.PrivateKey { + data, err := getDataFromKeyFile(file) + if err != nil { + log.Fatal(err) } - return rsaPub + return parseX509PKCS8PrivateKey(data) +} + +func getPublicKey(file string) *rsa.PublicKey { + data, err := getDataFromKeyFile(file) + if err != nil { + log.Fatal(err) + } + + return parseX509PKIXPublicKey(data) } diff --git a/src/unit-test/crypto_test.go b/src/unit-test/crypto_test.go index 12b97c9..904110a 100644 --- a/src/unit-test/crypto_test.go +++ b/src/unit-test/crypto_test.go @@ -86,8 +86,14 @@ func TestGenWriteKey(t *testing.T) { } func TestJWTRSASignAndVerify(t *testing.T) { - publicKeyPath := "./example_public_key.pub" - privateKeyPath := "./example_private_key" + // PK12 binary format + testTokenSignAndVerify(t, "./pk12-binary-private.key", "./pk12-binary-public.key") + + // PEM format + testTokenSignAndVerify(t, "./example_private_key", "./example_public_key.pub") +} + +func testTokenSignAndVerify(t *testing.T, privateKeyPath, publicKeyPath string) { authen := NewRSAKeyPair(privateKeyPath, publicKeyPath) tokenString, err := authen.GenerateToken("myadmin") diff --git a/src/unit-test/pk12-binary-private.key b/src/unit-test/pk12-binary-private.key new file mode 100644 index 0000000000000000000000000000000000000000..167504a080e8c6d3afdfa5e319fa9fcaea8e5b97 GIT binary patch literal 1219 zcmV;!1U&mNf&{+;0RS)!1_>&LNQUrsW5^Br2+u}0)hbn0MdJ2o6{=I zW24@tIRb3I$XuuQ+xwi1#)c>cScR z0{)dZv@~K?pr!@y0wA=#Jn%Dmt!&9Gh@#pO4^{affzgtU^RxGUZ)B!%wh`O(V6b_M zFp2Y9+jR+d6-Ph~RhY@9Yo}mj*hWaR$9j=y$;U{sKP%oiF}qLfShQ-DBNW%af_KW; z*qMk8)76qYX@vAaXEGRvRsfOrN^aCVZ-H_|0WD2*H2w=^r9t$unD)@cj1d+oF@(dr z>2|fKO`#fy6bjxhX6Qw|;__`Mw_`q@ypguzPS=$NyqIFb#ia~tcS{vWMuFNB6H`k4m{X~H zoD$T(h+N@*DD`Ptm7&oqjuu5CEvdk^u!*W+^%0Y2J!jc#=9ecTRD=#Y<$1bQFHjjO?9LsLqYj|NeLy#Y06v+dSZco&{jyX|J$g12V|K5>reDrp>8%i&c7s`HU=TqtnO^hscb`yl} z|5UtIAiYGS&3msf_j){3adW?#9c==EfdJoJ!q(`E6dm4bVF&5dFW|pr8?I}osF%RW zqKmKha3#2Eoguq7%@vwizpAf={87`rcXkz70lv)oRis5m`%9@dWMGsc7SW+6oQ+}{ z0`|o+w8NKUrXmY(XnHbY$PxTyiWXcw|0kwimCn7P^LlTgIuisnw~J9#t6f{=`#P8wUs2d`tW8P4!*7((;$+${3`-#$J>S*(n|gj zMIL%H+ujJ$td|i&m&wOuK!6NS9dR9I=xS2HhM0X#L`;@w&TwfdKCuJh}pLVx-9qv(PUdCF@Q?;G)MUmx(Fn2X*<56#ljCV91#9r#8V94fMjL z>8>tVu`oZSYlg~bx0U5CfDuONld_Sg@JzGXP8B!=tOW#~LcIT`^)x;B5-yEdm*f)T hDqO*(;0h+tB*hG&)C+otO8Z6sdl=4I-+fbc1Sd=xOw|AY literal 0 HcmV?d00001 diff --git a/src/unit-test/pk12-binary-public.key b/src/unit-test/pk12-binary-public.key new file mode 100644 index 0000000000000000000000000000000000000000..28ac44c06e4b1eaf8fdea2f5b08755f62e3c7bef GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>l(tBQ;(<;qlqu!=D z0&Ks?T&MTj`<#r%hA1h)aC?dIP1Nh9fDHx75HcJaMqT`&uz8CxiSt|A zbqRMBM?ei#n8~JVr(k5*Mo6>AdXZ?!$4Ie1E8aLUyHD&`v}%