-
Notifications
You must be signed in to change notification settings - Fork 11
/
helpers.go
195 lines (162 loc) · 4.19 KB
/
helpers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package siwago
import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"io/ioutil"
"math/big"
"net/http"
"time"
)
const APPLE_KEYS_URL = "https://appleid.apple.com/auth/keys"
//global cache for fast subsequent fetching
var applePublicKeyObject map[string]*rsa.PublicKey
//key object fetched from APPLE_KEYS_URL
type AppleKey struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use":`
Alg string `json:"alg"`
N string `json:"n"`
E string `json:"e"`
}
func init() {
applePublicKeyObject = make(map[string]*rsa.PublicKey)
}
//make request to APPLE_KEYS_URL to get the keys
func getApplePublicKeys() ([]AppleKey, error) {
var c http.Client
var req *http.Request
var resp *http.Response
var bodyContents []byte
var err error
var keys struct {
Keys []AppleKey `json:"keys"`
}
//make http client
c = http.Client{Timeout: 5 * time.Second}
req, err = http.NewRequest("GET", APPLE_KEYS_URL, nil)
if err != nil {
return nil, err
}
//perform request
resp, err = c.Do(req)
if err != nil {
return nil, err
}
//read response
bodyContents, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
//unmarshal into struct
err = json.Unmarshal(bodyContents, &keys)
if err != nil {
return nil, err
}
//return the keys fetched
return keys.Keys, nil
}
//get apple public key from the keys array
func getApplePublicKey(kid string) *AppleKey {
var keys []AppleKey
var err error
//get the apple published public keys
keys, err = getApplePublicKeys()
if err != nil || keys == nil {
return nil
}
//extract the key with specified kid
for _, key := range keys {
if key.Kid == kid {
//stop and return if found
return &key
}
}
return nil
}
//locally cache and get apple rsa.PublicKey object
func getApplePublicKeyObject(kid string, alg string) *rsa.PublicKey {
//if computed earlier, return the object
if key, ok := applePublicKeyObject[kid+alg]; ok {
return key
}
var applePublicKey *AppleKey
//get the key with specified kid from the web
applePublicKey = getApplePublicKey(kid)
//if ket found, contrust a rsa.PublikKey object
if applePublicKey != nil && applePublicKey.Alg == alg {
key := getPublicKeyObject(applePublicKey.N, applePublicKey.E)
applePublicKeyObject[kid+alg] = key
return key
}
return nil
}
//function to generate rsa.PublicKey object from encoded modulo and exponent
func getPublicKeyObject(base64urlEncodedN string, base64urlEncodedE string) *rsa.PublicKey {
var pub rsa.PublicKey
var decE, decN []byte
var eInt int
var err error
//get the modulo
decN, err = base64.RawURLEncoding.DecodeString(base64urlEncodedN)
if err != nil {
return nil
}
pub.N = new(big.Int)
pub.N.SetBytes(decN)
//get exponent
decE, err = base64.RawURLEncoding.DecodeString(base64urlEncodedE)
if err != nil {
return nil
}
//convert the bytes into int
for _, v := range decE {
eInt = eInt << 8
eInt = eInt | int(v)
}
pub.E = eInt
return &pub
}
//generate private key from pem encoded string
func getPrivKey(pemEncoded []byte) (*ecdsa.PrivateKey, error) {
var block *pem.Block
var x509Encoded []byte
var err error
var privateKeyI interface{}
var privateKey *ecdsa.PrivateKey
var ok bool
//decode the pem format
block, _ = pem.Decode(pemEncoded)
//check if its is private key
if block == nil || block.Type != "PRIVATE KEY" {
return nil, errors.New("Failed to decode PEM block containing private key")
}
//get the encoded bytes
x509Encoded = block.Bytes
//genrate the private key object
privateKeyI, err = x509.ParsePKCS8PrivateKey(x509Encoded)
if err != nil {
return nil, errors.New("Private key decoding failed. " + err.Error())
}
//cast into ecdsa.PrivateKey object
privateKey, ok = privateKeyI.(*ecdsa.PrivateKey)
if !ok {
return nil, errors.New("Private key is not ecdsa key")
}
return privateKey, nil
}
// Decode decodes base64url string to byte array
// just a wrapper function
func base64UrlDecode(data string) ([]byte, error) {
return base64.RawURLEncoding.DecodeString(data)
}
// Encode encodes given byte array to base64url string
// just a wrapper function
func base64UrlEncode(data []byte) string {
return base64.RawURLEncoding.EncodeToString(data)
}