This is a guide to using pyjwt
to sign and validate a JWT
using
RS256.
The trickiest part of doing this is knowing what the proper OpenSSL commands are to generate the RSA keypair. I demonstrate that below.
Here is how use the OpenSSL command line tool to generate a private key with a size of 1024.
openssl genrsa 1024
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCxlVm+DawmijNIDmAt11Sk5lRkd9691RyQevmr8u/eKvTV7eta
ZmGm2GmbuURYzwQfQ1+DFNrzu70wBLTyDrxCoX/vb/5hANwLJr5Eec6gGTF6/y4F
riRM1NEBZG9PnvmEPa1bfa27dnV5hz+GTIsCFCw4rXGI7c6ETg+v9t4HqQIDAQAB
AoGAMoekuYd6bJz2apJsm56h3yoK6WuSXcG+Fv5m/J5r0nO2pwjD5z0qnCcIJd9Z
q0t8iMjK7KmKg7/v3TH5qsa2mmUZ8UYMI5VmkwlKL4BD9mT67+ZAlqdLOHlrdrhG
u2FWR0PF5W2z06NqohWHepL01K7PLtxzaVzbQIwS4X3YsuECQQDrcqdSqj6gSF3Q
ZOZEMoES64iwAe/Fi4VmE6s0PEhoVXbbxFNxuvQPkylmoGs82ByAqltNk7p8G0Wb
hd+j3/KHAkEAwRWmkD189gSR/jLPEy/2fIkXhfe98OruPyG2yDsa3vp5wC2nrniG
udMxC4OJ1yXHGt4KBeOoOL94lX9p9UhQTwJAOx+SZs67ZTJm5HLB4/Qut1qP+2qx
FBEiEWz0++v7Xr+/VhZpwdBpgxO4PL4hz6iRF7ovrT5ggNO0WgZ3D0aoNwJBAKhx
T9ajnaEuCYLeJmJRxFGOc3QO1agX+3Id4kw5q858arxp18/QG5B/Glk2Dokfztu0
er/6hCXFe9fHyNMPm+cCQQCsqdhniYyaHPlyn/6bOwCcZua74JRZ3vNSkjAKzHan
WjSRdVvCHs8bY1Um0QZ+nHpT3QhcBPYyQoJArs8P1XBH
-----END RSA PRIVATE KEY-----
Now that we have this private key, let's take a look at what's inside and then extract the public key into a separate string.
Here is what is inside the keypair we just generated:
(Note: The sed
command below is needed because org-babel adds
leading whitespace to the string and OpenSSL can't deal keypairs
that have leading whitespace.
echo "$private_key" | sed -e 's/^[ ]*//' | openssl rsa -text
Private-Key: (1024 bit)
modulus:
00:aa:ad:9a:f3:09:8d:68:4c:44:09:5b:ab:d4:27:
7a:7d:0e:00:66:dc:e0:67:58:62:1e:78:d1:2a:7e:
de:b0:a2:24:b2:9b:03:5d:1f:17:d0:b1:9a:df:2c:
e7:e7:c3:17:56:90:f2:07:91:84:ec:84:76:10:43:
35:97:6d:af:03:e7:bf:0e:e3:53:8f:26:10:be:8e:
e2:12:ae:56:2d:34:14:0a:ae:ef:e7:01:3d:4d:7c:
f7:1e:c8:11:ca:6f:6f:cf:b7:67:e2:ac:54:77:20:
09:89:14:c0:66:21:cd:77:fa:91:25:66:59:31:c2:
c5:59:fe:0e:99:83:f0:56:b3
publicExponent: 65537 (0x10001)
privateExponent:
4d:57:b2:39:a4:00:82:5c:dd:0f:e8:8c:aa:ec:e0:
e2:be:6f:8c:2d:57:3b:3d:9f:e8:f3:12:c5:d1:0c:
14:ba:c5:2a:72:78:49:c0:87:48:38:d3:57:82:bf:
ec:14:4a:05:1e:55:ae:fc:50:61:e5:7c:a2:cd:f2:
01:16:e1:11:84:19:61:58:5f:fb:69:8d:ff:76:64:
fc:e8:3a:b9:28:84:9f:35:d8:9f:96:5c:c8:73:7d:
39:76:7c:99:a4:81:0d:50:c0:f0:51:45:9d:76:7a:
f4:53:81:0f:dd:3f:98:40:9b:a0:c5:cc:71:98:f3:
38:10:1b:b8:42:ff:4b:21
prime1:
00:df:89:a7:0c:cb:d0:8e:81:76:2a:a0:e0:83:ae:
70:69:4e:8b:ec:f2:ce:57:9b:16:d5:9a:72:be:66:
e4:b8:1a:84:e7:f4:28:2e:d6:70:50:f0:0f:85:ed:
67:33:d2:30:a2:2b:23:da:85:81:7c:2e:85:80:7f:
4d:74:a9:95:69
prime2:
00:c3:76:d1:de:11:ed:3b:16:97:ad:0e:65:22:ad:
1a:bd:95:a7:e7:f8:65:07:3e:ad:bf:30:d6:d4:9a:
04:9e:4b:5d:08:f2:2d:1e:5c:81:5a:04:80:f7:9d:
92:e6:54:e1:62:89:67:d1:f3:1a:ff:77:5b:a0:06:
5a:d8:79:3b:bb
exponent1:
16:2c:8c:72:9b:81:2b:b1:b1:ec:16:9b:4e:d4:ad:
f7:f4:3f:b3:18:7e:d9:77:db:f3:02:68:21:75:09:
79:2d:c0:43:56:17:ea:55:81:3c:b6:23:84:10:81:
ad:45:4b:67:ba:c9:ca:b2:75:9e:c0:ea:a3:4d:7d:
7c:76:1a:09
exponent2:
2f:ac:1b:23:7e:5b:cd:bd:84:e0:c0:52:0a:53:0d:
e2:8f:4d:94:56:10:cd:e3:8b:9c:c5:dc:9f:9d:b0:
e2:aa:9e:d6:3f:ba:a1:5d:0e:6f:56:09:de:5a:a0:
29:6d:2a:4b:4e:17:f4:2c:c6:b8:e7:f3:80:e4:0b:
e4:20:2d:61
coefficient:
00:c6:4d:e9:e0:56:90:c7:a1:c0:c9:72:c0:bd:17:
93:e8:fc:5a:64:5c:76:e2:74:67:e1:9d:3e:48:55:
bb:d2:c0:30:23:4e:21:5a:a8:01:52:11:53:f3:e2:
d4:61:2e:38:8c:18:b2:2e:87:02:c3:4d:b3:c8:64:
dd:50:39:33:06
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqrZrzCY1oTEQJW6vUJ3p9DgBm3OBnWGIeeNEqft6woiSymwNd
HxfQsZrfLOfnwxdWkPIHkYTshHYQQzWXba8D578O41OPJhC+juISrlYtNBQKru/n
AT1NfPceyBHKb2/Pt2firFR3IAmJFMBmIc13+pElZlkxwsVZ/g6Zg/BWswIDAQAB
AoGATVeyOaQAglzdD+iMquzg4r5vjC1XOz2f6PMSxdEMFLrFKnJ4ScCHSDjTV4K/
7BRKBR5VrvxQYeV8os3yARbhEYQZYVhf+2mN/3Zk/Og6uSiEnzXYn5ZcyHN9OXZ8
maSBDVDA8FFFnXZ69FOBD90/mECboMXMcZjzOBAbuEL/SyECQQDfiacMy9COgXYq
oOCDrnBpTovs8s5XmxbVmnK+ZuS4GoTn9Cgu1nBQ8A+F7Wcz0jCiKyPahYF8LoWA
f010qZVpAkEAw3bR3hHtOxaXrQ5lIq0avZWn5/hlBz6tvzDW1JoEnktdCPItHlyB
WgSA952S5lThYoln0fMa/3dboAZa2Hk7uwJAFiyMcpuBK7Gx7BabTtSt9/Q/sxh+
2Xfb8wJoIXUJeS3AQ1YX6lWBPLYjhBCBrUVLZ7rJyrJ1nsDqo019fHYaCQJAL6wb
I35bzb2E4MBSClMN4o9NlFYQzeOLnMXcn52w4qqe1j+6oV0Ob1YJ3lqgKW0qS04X
9CzGuOfzgOQL5CAtYQJBAMZN6eBWkMehwMlywL0Xk+j8WmRcduJ0Z+GdPkhVu9LA
MCNOIVqoAVIRU/Pi1GEuOIwYsi6HAsNNs8hk3VA5MwY=
-----END RSA PRIVATE KEY-----
Here we use the -pubout
flag to extract the public key that we
will use later.
echo "$private_key" | sed -e 's/^[ ]*//' | openssl rsa -pubout
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqrZrzCY1oTEQJW6vUJ3p9DgBm
3OBnWGIeeNEqft6woiSymwNdHxfQsZrfLOfnwxdWkPIHkYTshHYQQzWXba8D578O
41OPJhC+juISrlYtNBQKru/nAT1NfPceyBHKb2/Pt2firFR3IAmJFMBmIc13+pEl
ZlkxwsVZ/g6Zg/BWswIDAQAB
-----END PUBLIC KEY-----
This is a little Python list comprehension that will strip out leading whitespace from input key:
key = "\n".join([l.lstrip() for l in input_key.split("\n")])
And here we take the private_key
we created with OpenSSL and use
that to create a JWT that is signed with RS256:
key = "\n".join([l.lstrip() for l in input_key.split("\n")])
claim = {'test': "hello"}
import jwt
token = jwt.encode(
claim,
key,
algorithm='RS256')
return token
Now, taking that result, we can decode the JWT we just generated and verify the signature:
key = "\n".join([l.lstrip() for l in input_key.split("\n")])
import jwt
return jwt.decode(token, key, algorithms=['RS256'])
{u'test': u'hello'}
In the PyJWT code snippet, both the encode(...) and decode(...) part use a vague
key
(orinput_key
). Are they supposed to be something like private_key and public_key, respectively, as demonstrated in PyJWT's official doc?