Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
dex
Commits
0a3aabc8
Commit
0a3aabc8
authored
Oct 14, 2016
by
Eric Chiang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
storage/conformace: add conformance tests for keys
parent
ade27b3d
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
295 additions
and
1 deletion
+295
-1
conformance.go
storage/conformance/conformance.go
+61
-1
gen_jwks.go
storage/conformance/gen_jwks.go
+110
-0
jwks.go
storage/conformance/jwks.go
+124
-0
No files found.
storage/conformance/conformance.go
View file @
0a3aabc8
...
...
@@ -8,6 +8,8 @@ import (
"testing"
"time"
jose
"gopkg.in/square/go-jose.v2"
"golang.org/x/crypto/bcrypt"
"github.com/coreos/dex/storage"
...
...
@@ -31,6 +33,7 @@ func RunTests(t *testing.T, newStorage func() storage.Storage) {
{
"ClientCRUD"
,
testClientCRUD
},
{
"RefreshTokenCRUD"
,
testRefreshTokenCRUD
},
{
"PasswordCRUD"
,
testPasswordCRUD
},
{
"KeysCRUD"
,
testKeysCRUD
},
{
"GarbageCollection"
,
testGC
},
}
for
_
,
test
:=
range
tests
{
...
...
@@ -42,6 +45,14 @@ func RunTests(t *testing.T, newStorage func() storage.Storage) {
}
}
func
mustLoadJWK
(
b
string
)
*
jose
.
JSONWebKey
{
var
jwt
jose
.
JSONWebKey
if
err
:=
jwt
.
UnmarshalJSON
([]
byte
(
b
));
err
!=
nil
{
panic
(
err
)
}
return
&
jwt
}
func
mustBeErrNotFound
(
t
*
testing
.
T
,
kind
string
,
err
error
)
{
switch
{
case
err
==
nil
:
...
...
@@ -278,8 +289,57 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) {
}
}
func
testKeysCRUD
(
t
*
testing
.
T
,
s
storage
.
Storage
)
{
updateAndCompare
:=
func
(
k
storage
.
Keys
)
{
err
:=
s
.
UpdateKeys
(
func
(
oldKeys
storage
.
Keys
)
(
storage
.
Keys
,
error
)
{
return
k
,
nil
})
if
err
!=
nil
{
t
.
Errorf
(
"failed to update keys: %v"
,
err
)
return
}
if
got
,
err
:=
s
.
GetKeys
();
err
!=
nil
{
t
.
Errorf
(
"failed to get keys: %v"
,
err
)
}
else
{
got
.
NextRotation
=
got
.
NextRotation
.
UTC
()
if
diff
:=
pretty
.
Compare
(
k
,
got
);
diff
!=
""
{
t
.
Errorf
(
"got keys did not equal expected: %s"
,
diff
)
}
}
}
// Postgres isn't as accurate with nano seconds as we'd like
n
:=
time
.
Now
()
.
UTC
()
.
Round
(
time
.
Second
)
keys1
:=
storage
.
Keys
{
SigningKey
:
jsonWebKeys
[
0
]
.
Private
,
SigningKeyPub
:
jsonWebKeys
[
0
]
.
Public
,
NextRotation
:
n
,
}
keys2
:=
storage
.
Keys
{
SigningKey
:
jsonWebKeys
[
2
]
.
Private
,
SigningKeyPub
:
jsonWebKeys
[
2
]
.
Public
,
NextRotation
:
n
.
Add
(
time
.
Hour
),
VerificationKeys
:
[]
storage
.
VerificationKey
{
{
PublicKey
:
jsonWebKeys
[
0
]
.
Public
,
Expiry
:
n
.
Add
(
time
.
Hour
),
},
{
PublicKey
:
jsonWebKeys
[
1
]
.
Public
,
Expiry
:
n
.
Add
(
time
.
Hour
*
2
),
},
},
}
updateAndCompare
(
keys1
)
updateAndCompare
(
keys2
)
}
func
testGC
(
t
*
testing
.
T
,
s
storage
.
Storage
)
{
n
:=
time
.
Now
()
n
:=
time
.
Now
()
.
UTC
()
c
:=
storage
.
AuthCode
{
ID
:
storage
.
NewID
(),
ClientID
:
"foobar"
,
...
...
storage/conformance/gen_jwks.go
0 → 100644
View file @
0a3aabc8
// +build ignore
// This file is used to generate static JWKs for tests.
package
main
import
(
"bytes"
"crypto/rand"
"crypto/rsa"
"encoding/hex"
"encoding/json"
"go/format"
"io"
"io/ioutil"
"log"
"text/template"
jose
"gopkg.in/square/go-jose.v2"
)
func
newUUID
()
string
{
u
:=
make
([]
byte
,
16
)
if
_
,
err
:=
io
.
ReadFull
(
rand
.
Reader
,
u
);
err
!=
nil
{
panic
(
err
)
}
u
[
8
]
=
(
u
[
8
]
|
0x80
)
&
0xBF
u
[
6
]
=
(
u
[
6
]
|
0x40
)
&
0x4F
return
hex
.
EncodeToString
(
u
)
}
var
tmpl
=
template
.
Must
(
template
.
New
(
"jwks.go"
)
.
Parse
(
`
// This file was generaged by gen_jwks.go
package conformance
import jose "gopkg.in/square/go-jose.v2"
type keyPair struct {
Public *jose.JSONWebKey
Private *jose.JSONWebKey
}
// keys are generated beforehand so we don't have to generate RSA keys for every test.
var jsonWebKeys = []keyPair{
{{ range $i, $pair := .Keys }}
{
Public: mustLoadJWK({{ $pair.Public }}),
Private: mustLoadJWK({{ $pair.Private }}),
},
{{ end }}
}
`
[
1
:
]))
// Remove the first newline.
type
keyPair
struct
{
Public
string
Private
string
}
func
main
()
{
var
tmplData
struct
{
Keys
[]
keyPair
}
for
i
:=
0
;
i
<
5
;
i
++
{
// TODO(ericchiang): Test with ECDSA keys.
key
,
err
:=
rsa
.
GenerateKey
(
rand
.
Reader
,
2048
)
if
err
!=
nil
{
log
.
Fatalf
(
"gen rsa key: %v"
,
err
)
}
priv
:=
jose
.
JSONWebKey
{
Key
:
key
,
KeyID
:
newUUID
(),
Algorithm
:
"RS256"
,
Use
:
"sig"
,
}
pub
:=
jose
.
JSONWebKey
{
Key
:
key
.
Public
(),
KeyID
:
newUUID
(),
Algorithm
:
"RS256"
,
Use
:
"sig"
,
}
privBytes
,
err
:=
json
.
MarshalIndent
(
priv
,
"
\t\t
"
,
"
\t
"
)
if
err
!=
nil
{
log
.
Fatalf
(
"marshal priv: %v"
,
err
)
}
pubBytes
,
err
:=
json
.
MarshalIndent
(
pub
,
"
\t\t
"
,
"
\t
"
)
if
err
!=
nil
{
log
.
Fatalf
(
"marshal pub: %v"
,
err
)
}
tmplData
.
Keys
=
append
(
tmplData
.
Keys
,
keyPair
{
Private
:
"`"
+
string
(
privBytes
)
+
"`"
,
Public
:
"`"
+
string
(
pubBytes
)
+
"`"
,
})
}
buff
:=
new
(
bytes
.
Buffer
)
if
err
:=
tmpl
.
Execute
(
buff
,
tmplData
);
err
!=
nil
{
log
.
Fatalf
(
"execute tmpl: %v"
,
err
)
}
out
,
err
:=
format
.
Source
(
buff
.
Bytes
())
if
err
!=
nil
{
log
.
Fatalf
(
"gofmt failed: %v"
,
err
)
}
if
err
:=
ioutil
.
WriteFile
(
"jwks.go"
,
out
,
0644
);
err
!=
nil
{
log
.
Fatal
(
err
)
}
}
storage/conformance/jwks.go
0 → 100644
View file @
0a3aabc8
// This file was generaged by gen_jwks.go
package
conformance
import
jose
"gopkg.in/square/go-jose.v2"
type
keyPair
struct
{
Public
*
jose
.
JSONWebKey
Private
*
jose
.
JSONWebKey
}
// keys are generated beforehand so we don't have to generate RSA keys for every test.
var
jsonWebKeys
=
[]
keyPair
{
{
Public
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "8145b5b9243c41459a8fdaa12acbd371",
"alg": "RS256",
"n": "34ls8E4onyEU_JKcxl8BMu2N6hK_D6aG2tOuCHJ_ka4rom8NmdJGdOQPC_fvKhcAxWeDktdAPislTT76Q4iMCC7DbM1aQhgRMaecKHBagc5ue2kSPM3oZPLqe6X-CxdxGTfXAvFIZM9JZTbQeJPcXFdn28iZ086xWPMdQKY5QTRKtoHQSN6EAQuuiuZsXrAC3lBZmE4tda6NoeYLb0UayGqiiFmtoIFJQ4NecI-EECT-mcjkPGWG0Ll5dCIUhGDl8sQSUrmBuaTDpPEzLGo-UtM3ay7AN0gOVN0mLIk2oyroXcVOA626LYNLVU0mz9PDpdkhWBeUfLL6i4HjUS3RaQ",
"e": "AQAB"
}`
),
Private
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "f547defc90b34ec08caeb8b294591216",
"alg": "RS256",
"n": "34ls8E4onyEU_JKcxl8BMu2N6hK_D6aG2tOuCHJ_ka4rom8NmdJGdOQPC_fvKhcAxWeDktdAPislTT76Q4iMCC7DbM1aQhgRMaecKHBagc5ue2kSPM3oZPLqe6X-CxdxGTfXAvFIZM9JZTbQeJPcXFdn28iZ086xWPMdQKY5QTRKtoHQSN6EAQuuiuZsXrAC3lBZmE4tda6NoeYLb0UayGqiiFmtoIFJQ4NecI-EECT-mcjkPGWG0Ll5dCIUhGDl8sQSUrmBuaTDpPEzLGo-UtM3ay7AN0gOVN0mLIk2oyroXcVOA626LYNLVU0mz9PDpdkhWBeUfLL6i4HjUS3RaQ",
"e": "AQAB",
"d": "3rABHsQ-I4jJZ3SHSfeLMjkFj5JtVCIJZiZK0Y9_Fpn0TjVjz0Fzfy9S7hFo6P1Rf1bH9JkLHuPMnU-H8Y8uMVikxtcse3uOZXEcWAzVnUsRNVBPItPeF_MHNXb_xfzsZrsCL6Q_Am6eJ36b4AMtG7DXflQxKphWhM5s7eKqVxDrkhaDPnALLRFjCvUZ_myQQ3Upn7gMgAbvfIY1fn9rXW_4CfxbxhcPJW5IOcu6bPvpQlfuFkXjF-gGCiNf5kv6Db0lpDOKX5l5T-KFGQ0dIOdasm8vL2GxCKZf55rKRCt0a28fwwH2p94ja-1qtPTc34V8F26LyVRgQgD3e-0aoQ",
"p": "_WoAr3sgL5yfaqBL38yqx4hqSPZGdR6xTS64rhgZaVg14_W6xYmlPI7PmVBRW45Fk4tXhXjv9oMZH9HGrH2v4yqXLEq0gJr4VAPvRaN6p_kb_eCfLHCbNCYBAPNVUdFpOTvOmh7m0zYPrku7DZDnZQEN_A9hYcufjy0em-lV6Tc",
"q": "4dFfwyYQmns1xwVEPABxpazk6nAluS-7yYSAc9A8D25nqm0mNWdPJvmpJS02xSDjIGfe0FtMr1XlPm3XHdUlIu2Z9Ex-J-kcs3lfs2UKmleQqJRXK4MahAEIV3vp0zG47hAJyzE3Oh4sVLFr3ZK9_-SenolCFv5eIikWa3Xg6l8"
}`
),
},
{
Public
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "3a9365e41b114ec1b9288b214196e158",
"alg": "RS256",
"n": "t3TrxLN5_z-x5X9kebkoPnoYnGAPqAXOVCGBTxcAqev_P8t6SyyeeITDiePhCctYp5dO-WHRkB7_BkUeHZOgoyCBarDkDifQSG7MCtlYDm0yiSij_0vqzJQx-6zlXb5ypwO0P1sAXrO_nO87u69w5yaKf0yEJMpSjU8BDKQ__nskZP2QJJsYwOeAI9aAM2oP8r7Im8KzLy9-mnFSqypxBnL24hFNzKOS_GyHs0tPLjVY7JNDtDOkwPQIQFzsdZSY88n6uYvV-MGu3O-Y3-xLwUqMlJOXFskhmp1AOUnb4JgQ9wEaZ7088PY3Ak0eZkrg2FQ3XRHSWhUCOb2xL5iTvw",
"e": "AQAB"
}`
),
Private
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "c79418aaf8ee439bb2b0e28672d71584",
"alg": "RS256",
"n": "t3TrxLN5_z-x5X9kebkoPnoYnGAPqAXOVCGBTxcAqev_P8t6SyyeeITDiePhCctYp5dO-WHRkB7_BkUeHZOgoyCBarDkDifQSG7MCtlYDm0yiSij_0vqzJQx-6zlXb5ypwO0P1sAXrO_nO87u69w5yaKf0yEJMpSjU8BDKQ__nskZP2QJJsYwOeAI9aAM2oP8r7Im8KzLy9-mnFSqypxBnL24hFNzKOS_GyHs0tPLjVY7JNDtDOkwPQIQFzsdZSY88n6uYvV-MGu3O-Y3-xLwUqMlJOXFskhmp1AOUnb4JgQ9wEaZ7088PY3Ak0eZkrg2FQ3XRHSWhUCOb2xL5iTvw",
"e": "AQAB",
"d": "T7-y0dIXQV8l7RbAza0wkmAvHKMhiy_i7m2WMZRVRIiDb-77HXyq8sb73ZBC_if4RPogaYYdPCJNSCN5oO_Qz7jMqV119bVW9HW9myW6AqNzaW5SRCNzUTVGuRoCpwqn-nRAwZ3EfmZy8DyK4d61HLaDVC0l8HxHAIiMcztfWjbfD2LjwWF2hF5VRG2-haDfT6Kwtz0zEXblvYxyPqVyKOFtuWDlzX8iP8_ryWaChpR-jTmwtm7663wcu4M9teMkdgubCIqkz0LLtd-97ZUM2ti70WO7AEqE6p1evnjfYt4HZpQlsn0psrgGLvX2oCIvmPQMfTjzmtsEC51F5CU-yQ",
"p": "4xi5OdCP9n1ivD3CuMhcaoMrwkC1yVdYnJwaNXjIyuSUT0i_QmuRpViydpZsfiYEoNNczL_PwxlDNdl2ccbelBuoEDbrvAfz0G0-YVYuLJoEKQs_OjenIn_6AZlmn7zSQ0LjoZ1tTjOaKuueB2b8RVtF2pbZ_o1ApyWd3q6QjyU",
"q": "zs5SF-jdzP9xThPTEmAa2yh6SI48KuwVwWXGjOQZThXVEfwo-iZNevPjg3b6gwY9fKi71-J75c1ng0QrgdDuRIackHFpSLaWgcIpN31-uyZl5X-uxpBZON1HeiYT8J2JhgbA9ZJ0_SUq3j4YSrFEGKSpBi741mqwS9CZ6NSN5BM"
}`
),
},
{
Public
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "4c267fc23c7b44d6973a1722b7201849",
"alg": "RS256",
"n": "yeZexEF1gOXd71iz9jRQR3EhgM2-o3mVO4O1fJYYQTh5APfrrbMhOGLvgK06vytREiY9_1awL7YfEnZzQynq9WTZpkwlAhYujHYf1RbGPeoXJS2cXKThfIhbeITEyhfepqzwU_f-RhvaLS3bydDi7F74oTO9njtLkGV2qNHH3B2uTFBy2G8VmDeHNQrUa868LQ9omrmWFkLnoZOoVPiLZD-5aZXOKJ0In5sg9B1EX1oaF-xejCTBX_8EJvvvKXH-GUZnHc3g3Rf3k4iXCJi8VMyjA8we3fgP8jp2P3Ofv6VOKG3vh8j5lI3ys_rctc2fu6CaNWNNZs9wbjpDVPuc0w",
"e": "AQAB"
}`
),
Private
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "eec6ee158cb34d699be4baff419da383",
"alg": "RS256",
"n": "yeZexEF1gOXd71iz9jRQR3EhgM2-o3mVO4O1fJYYQTh5APfrrbMhOGLvgK06vytREiY9_1awL7YfEnZzQynq9WTZpkwlAhYujHYf1RbGPeoXJS2cXKThfIhbeITEyhfepqzwU_f-RhvaLS3bydDi7F74oTO9njtLkGV2qNHH3B2uTFBy2G8VmDeHNQrUa868LQ9omrmWFkLnoZOoVPiLZD-5aZXOKJ0In5sg9B1EX1oaF-xejCTBX_8EJvvvKXH-GUZnHc3g3Rf3k4iXCJi8VMyjA8we3fgP8jp2P3Ofv6VOKG3vh8j5lI3ys_rctc2fu6CaNWNNZs9wbjpDVPuc0w",
"e": "AQAB",
"d": "IOFck5eZfElzMFSA0lrIrCnXa_OV1WeqjwuvFcAX6R86TZcSkbI3echa-ti7VYDHbi4-MIQ8oziErOwPb2O3OQmYjIWgDUvxfryKCJjx5glmhY59BXVwp2hJhUISDlt-ziQh63ratS46BNuQDLjxC8-XrCESA1_iuXxcq7emVclRKN2DpGehf2bZyjcZy-OEwvL1jLsvoY2jmY_2JOT4nFLqoelg5vENj69p8IR9Bpdzp0urngLZJ4-HqFGyfx3tEo4ZUF1M5xnoycBc5LMZjmElK66rjBRWPq9UwZwfqaeQh6wEA9siYw1V9yrNRUkq3Q6BErbXNDKBV36bRIiaIQ",
"p": "_YirCr3Sfs9FkEFFMNsTZ2Wv8e5napONPtg1WUYOxG36k65EkPtlmZLWmiwmBk6592oND_S5WvbW4BbX5lRbEvNiRy9coVPst6lOOnLe69GJoI_GxoRyu_94qIS-VNPSQkyw4gfA1M-lMdfKpaTMv7fvVolvmDs5xN_fmXpl06M",
"q": "y90gdyUcYzDX1u3-fCINzXbDcr80QEO3bjuG8p7feaYY2MP51t6j6MisNsQqcGKY7xFhpc-z8_cEIg1HJ3FSly-yejPj8RGavPX6NVGVHDNGwxxnm_i3kf-4MuDxwRSSHMlgVNAXuoH-3iicz-bNTVYM-5bYucZMvZHC6Ur2JRE"
}`
),
},
{
Public
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "e10385c5384046f395fc6d9027db2f35",
"alg": "RS256",
"n": "299cgJgPiu9CK8hGgQw3j8e-Y_u4-Tm6WXKOFHdjCUPV5EAWMOa34cQNt75KN8pxlIcnujnU6TpH4OPRCw1gA44rrk_uczIEULsTnt6UFuMtUY2r-2UW2BWg5rEHyLcNX_QCA80T9DVSxsWeN8S23YcVk9fVputIRU7ee7auOx3b6K3pkoQJBVUk-_ndaqwlX-JU2CQG52CH91CrDzN0WGUPrhMZOdL7ybv94l5ztBrnjaQupkt0FxTA1_m_tXTvxIgzzegaqXrJ1mJM-z2TxPUJUc_04JaGilPUkxU780jk_03d46Op-pdElgbZ52C9JT9b8nRnA-vHq4e2whY8Yw",
"e": "AQAB"
}`
),
Private
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "8165cc507cd1492394be64575dfa8261",
"alg": "RS256",
"n": "299cgJgPiu9CK8hGgQw3j8e-Y_u4-Tm6WXKOFHdjCUPV5EAWMOa34cQNt75KN8pxlIcnujnU6TpH4OPRCw1gA44rrk_uczIEULsTnt6UFuMtUY2r-2UW2BWg5rEHyLcNX_QCA80T9DVSxsWeN8S23YcVk9fVputIRU7ee7auOx3b6K3pkoQJBVUk-_ndaqwlX-JU2CQG52CH91CrDzN0WGUPrhMZOdL7ybv94l5ztBrnjaQupkt0FxTA1_m_tXTvxIgzzegaqXrJ1mJM-z2TxPUJUc_04JaGilPUkxU780jk_03d46Op-pdElgbZ52C9JT9b8nRnA-vHq4e2whY8Yw",
"e": "AQAB",
"d": "xh587o6WKr2uZV8gUHXettroLpWKtl-TD7hOWBi_j4ClgfdRR50NggwzxCZeH-l18LzcSkyEEefnDriZC5lws6NurrHtjbU6-Dep1VSAIiNwGXVLy8nqDKlog5ZvCigPkC-BhUVMPpexz9QP3faORAzNn5szNCX7yB_qD5WrZy20AUEoWtGPgxGW6xf5Lgu6zg2uQEEB1Z0hKjHV9seIiuQooMrSzpS1D7BLSTHOvM2Y2lXvQQokc3uQXnyT_soHPjHl00bcuJLJaRCmyHRTol7uh9MNe67eMy7pHYmmlwOvTDfW6meKCgoEXd1wKIrS9VRY7WP36ZRpJH6qv8vceQ",
"p": "7sWEsknUaSlAJ-bGhsuFr_j15zupV9O-DLnLobASm4Z7Ylt1HhtPN1NCVzYFTCtltPBE_CXGaAPqw3wiERK3tgYSLV8yk57sU1H28Zsq65A1B-vdlO69-F_6djiGegYKTOO4CXt0VYB4hJ6Trwx_BNJmrAD_Ykjqsp5sR0gOrqU",
"q": "67y_hzbi81IH2DxmHTQOfHgcLYe-TnrEQLGLQtfx8J0J_REf_fLBDL-pt_jy6WIvTAb-LgUIcieiXfhni1nPUw0f_I1SDNv02EYvP0vkfyQdJBR6sLi4jv0mpqyQxvGif9B4eM9Qjngm2Jclj3-el-RkMZOUyf3zGTNGLI3MmGc"
}`
),
},
{
Public
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "941861b40500430da0d09ec213e00832",
"alg": "RS256",
"n": "ub3SiNK-uIvSrUTyIPm1cITzuqPX_CIa6nZTDTP1tJ6PP_KufYz2eGLj9jppWLo_J7XQfKfIAKvET8Mq4HEcLQpNRN90KNyGML17JJtSgYJeLuB38BnalVUxpnycPKeGgoNJMu6t8tKYOtOfxtqTA6x8MnqMeify1cvEc5Tr4QmKjcLLHKcr1yMR7kG48i586bLdchtIBYeB298WXbQaKrgsEjZA0E1exfMnYHyvN12lMBxwhOJtcFu3mngZ7vTh179UKsP3yD8IdO5ITe_RIOmnUKuynW3PdkRUzCK5gS-xuqueGqEzJVIKBv0Hfom3eyDW5DjxpIZxlqkGhGyeNw",
"e": "AQAB"
}`
),
Private
:
mustLoadJWK
(
`{
"use": "sig",
"kty": "RSA",
"kid": "c4c09817da9a42ae8d850aaba7b7cd82",
"alg": "RS256",
"n": "ub3SiNK-uIvSrUTyIPm1cITzuqPX_CIa6nZTDTP1tJ6PP_KufYz2eGLj9jppWLo_J7XQfKfIAKvET8Mq4HEcLQpNRN90KNyGML17JJtSgYJeLuB38BnalVUxpnycPKeGgoNJMu6t8tKYOtOfxtqTA6x8MnqMeify1cvEc5Tr4QmKjcLLHKcr1yMR7kG48i586bLdchtIBYeB298WXbQaKrgsEjZA0E1exfMnYHyvN12lMBxwhOJtcFu3mngZ7vTh179UKsP3yD8IdO5ITe_RIOmnUKuynW3PdkRUzCK5gS-xuqueGqEzJVIKBv0Hfom3eyDW5DjxpIZxlqkGhGyeNw",
"e": "AQAB",
"d": "29bQWSEWm1bjBDGWY3EqTwMNdtp1yPaU5O0nX3kgV6dT5VxXKkKtdc-WANkh1uKZ3WZUXTY4gpLKx504Im2965FF4z6XPcXFDes21R0BikfDMbh8PLJdBGLRYTwbr66YheDdwmq9d6nKg9X2RmZtmuuMFDL4EZ02zdVfr22TwcSCghC2gnV6CpHHeEatJBWbK1yE6cHqCeY9UTc_QnXmbZ0TYsQi4qCV1HqTJKZDtkzqZMPvMB5EP_my_SCxcfcIzt6qqujmuXCFiS658Up-Z4W5s0RINLoPmePG8zJVFBmWrQ8xiykCeL8z9XSvXoEo6ZJJC-KSjI6s-KsCfQqZ",
"p": "8LzUJM2YgP7zG618rrFTav3gB2t1yMwFJy9d3J-pOkVFUq-4-74qEZz6H2RTUw7Ae5XEYdVIbRRQInpo0qO2MfLW8vtRexUNFFt1pBiVykq-KdkWcwPETyRD-huEEqswBhg33lFTUrY7BXRukbfNmVY7YfdagIJ5LZU0I-nGMqs",
"q": "xYRoIFTTiXitKBFo0vvHAadqVV8gJq8bCxJZ4lFMpADlU-S8Me7aPmkhPmCDaw-ii940S46bTp9ueh6EJCttmG3cJm8r4YzjK-H1dnqeF_3dpq2pimVFlFILBKWojUHHWC4n0d1IVwdf8-xnDSiUzl9roFZV5IPy4mW1HMTZ4qU"
}`
),
},
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment