Da Vinci Clinical Data Exchange (CDex)
2.1.0-snapshot - CI Build United States of America flag

This page is part of the Da Vinci Clinical Documentation Exchange (v2.1.0-snapshot: QA Preview) based on FHIR (HL7® FHIR® Standard) R4. The current version which supersedes this version is 2.0.0. For a full list of available versions, see the Directory of published versions

Signed Searchset Bundle Example

Page standards status: Informative

This is a Jupyter Notebook which uses openSSL, Python 3.7, and the Python jcs and jose libraries to create a JSON Web Signature (JWS)(see RFC 7515), attach it to a FHIR Bundle and validate it. Its source code be found here

Although self-signed certificates are used for the purpose of these examples, they are not recommended for production systems.

Sender/Signer Steps

1. Generate RSA256 public and private keys for signing the bundle

DO THIS STEP ONLY ONCE

2. Create a self-signed certificate for authenticating the signer

create the public and private keys and cert using openssl on the command line.

  1. pre-configure the self-signed cert with a configuration file
[req]
default_bit = 4096
distinguished_name = req_distinguished_name
prompt = no
x509_extensions = v3_ca

[req_distinguished_name]
countryName             = US
stateOrProvinceName     = California
localityName            = Sausalito
organizationName        = HealtheData1
commonName              = Eric Haas, DVM
emailAddress            = ehaas@healthedata1.org

[v3_ca]
basicConstraints = CA:TRUE
keyUsage=nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = www.healthedatainc.com

2. generate the public and private keys and cert

!openssl genrsa -out private-key.pem 3072
!openssl rsa -in private-key.pem -pubout -out public-key.pem
!openssl req -new -x509 -key private-key.pem -outform DER -out cert.der -days 360 -config cert.config

For the purpose of this example display the keys (normally would never share the private key)

!cat private-key.pem
!echo
!cat public-key.pem
-----BEGIN RSA PRIVATE KEY-----
MIIG4wIBAAKCAYEA6SnEpKADOrFttfr1k3iFThsddDFmrEMu1R4nes8qlwATPs53
wZ13p8lNI9RU7z5kXzg6Dg11bj1MA6JoQh4fm3JVvSjNqOet3MUShLwZ2h67I8Oc
jZsTuWIxEW4bR3UHqpLXcN1WBEUfR6MSztxZLM0dvdh0weMVt8lpVd4E5DEKMz0n
CSh92xvD6qugGDyewuGASJVEnQTFZd6p3hH4O37sYhX16H3U1Zu6zIohu1/c+Nz3
4pamnorH5rUcQJUcBDV6x9zrzgz8i9K05xvArGwF2FSDJnXR3uGRfaZYfebI+KTE
4S7XCV/6PVxy44exJmcoCR1hEKuD8BcGXZm3H4Qpjq/PB/AW1K7v+1es27BtTdQl
pZ3ZW1c5y/tyDDq/JF2h3Gp6n3JIBVeBK534xSatTiGgJrDI/OcTJI8ly8nCy/7u
Z9qOgYPd/1EX6rjqEiBjgkduQ2mc6cNpN7O6BPXTMBFl7X14GLYdm5Y8ubRR9Bo6
TpMdO4+2U+R58Z5bAgMBAAECggGANf/QZPgSB2PULtNCULcW2HH7Lk/KoZaloAHt
zslv6azAyEj0/0hCz/8U+HlSel4OzOauu1ZunetgUW8pijaDx3KBXN+4UafmYjzZ
/xe5PQTk/nFtLnZ96O9OweSoOLJn5h8/+gmoxDBmACdKUdJCbNfMTY117Pl0rC3f
UV2r8FVTMW62Pa69ByO1CgJZf4N6mVO0bBr12w+hz+fzm1S6Er1gbY78dq29vwLk
Dj7ndQfMm25Bkp6lVA58IXhMZMCjXSbei1aHxYBXAcAJP5bouiAxqnb2rZ5QM1W1
CpLuJ0HbGGl0y6aWZenV/C5JEDKqqh62KUGPnTeJLgpJpnuK59y7K+0dcFFKji5C
0d61lTaOKxHz3X11043j9+pJYJ8bEpxwimO+1i6boBNrB++i5cC8UKxK0Zw6o3HW
Xfko57EJR6oCiWeorgHZTF6wud4WlRkOMrt7EkrhbPQF8lcahCKGCwI63wxcCtE6
Wf784Dto+kQSQj6+G2V385utiXExAoHBAP95t1kQdTQl3ASwA3wvnU1oRzsj83DS
Qrg2vhJlhTUwKIsxKYIvL/KQQFsd4/ejr5nGurlAcFsRlA6cR94LXL2DWvvy52uP
r1UeOPeJ7DiFu2IY8ehtX9wEbt1dLYkgwTmkwASQIJsaNCgMH+1y5jcWdWGmHqf8
Y4z3/JerrT1FRT/Sgsp34HrzeCAajyRLK/qzDU1ieXYWVMnv+ZGEl5DFMvFRf0dP
IJHT7uuHdAvXw1tRcvsYtGIdiHyNDGjcpQKBwQDppFL2n50Fx3oWanXIspWS4Whq
PgBm2wTz5o+LGQIRj0m6qD5q+/mXdxxzj8zY3qlkQj1YGT2Bog9cYL2+PuNqnHeq
coi6uVf0zruq9XcrCoYs5sT4GT57LdRQK3xtGXURFbrVc/wjn7rU1/XhqThQADGx
cHvUVc37Pkvirf0/hAkW3KwOBEsXn8IYJ5JydBOm957NvoEAGCcI9tc0FM7xl4I7
CMzSYl+i/tXy/sec45Y514LXun8Tvm09uj8Qnv8CgcBX7HDwgxyDLQBy0IzygYSW
rmvNa8sNqh9yPMzfkfbtXjyl4u6RMmJDDehIM5pQkRIPT3jV5tqETSFygdCuF6T8
SCfZDDkfKJ1EIxmh/+K/dS8PTNx8jJ/wHcp1/up8BjzZ9Nxs9ZlzJ+qJWdrnIqMe
lGkjiUOFtvQgAPz6ygfeTWfO1klTGzyzs/VEvz9CU2i4aEUMu/ZncLoPobp3nXV4
SpYEvXKbfR0Ncy4H+18x8Dj4xahXyKHUHqvpibWapU0CgcEAinvgxNd0neFPZwvx
BmV7rJS4bAiV+mElPV/SgzLue/P/Uoi4fncdyH5MOd7pHz5TDt8INzi2sSiajDm3
HZVu/FkDwN6kVnDXRn5m0/0shjF1uBWMeDWWSDKw3lf4Jz5omhjUJgLaV08s0U20
Ku4/N4P18GNoskGtlpalKhXQvp4HOSrRPHmk/Lqvs9t4vSg6IcQxt2eMVL78HB8b
DX6r7pzMDyu3I5g1cYo0zBPhwwdOmrg3kKC1A8HiRC0phjOHAoHAbayWoC7XWsKx
1XzirFof87u47oEAcu0VlOJOwCr270J2Q1jyLMRCvgNQYvZz6nTJt0jcXazZrBTQ
jkwHgb7kFFSTqrE3Uj8Bmm6O+WnoY2+zi4leSe8yz3SRpLL1w9S1p40XQGLJhIX+
GJr5shr9osadWap/vzl244Do4Od/NMehLl6ibyu1UteRexDp8JjyUBwAAC50kMYP
OavUzEG9Ymb4t4yNjaso6C7ZNVcbJTJeI1A153p0uH3xJqFefai2
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA6SnEpKADOrFttfr1k3iF
ThsddDFmrEMu1R4nes8qlwATPs53wZ13p8lNI9RU7z5kXzg6Dg11bj1MA6JoQh4f
m3JVvSjNqOet3MUShLwZ2h67I8OcjZsTuWIxEW4bR3UHqpLXcN1WBEUfR6MSztxZ
LM0dvdh0weMVt8lpVd4E5DEKMz0nCSh92xvD6qugGDyewuGASJVEnQTFZd6p3hH4
O37sYhX16H3U1Zu6zIohu1/c+Nz34pamnorH5rUcQJUcBDV6x9zrzgz8i9K05xvA
rGwF2FSDJnXR3uGRfaZYfebI+KTE4S7XCV/6PVxy44exJmcoCR1hEKuD8BcGXZm3
H4Qpjq/PB/AW1K7v+1es27BtTdQlpZ3ZW1c5y/tyDDq/JF2h3Gp6n3JIBVeBK534
xSatTiGgJrDI/OcTJI8ly8nCy/7uZ9qOgYPd/1EX6rjqEiBjgkduQ2mc6cNpN7O6
BPXTMBFl7X14GLYdm5Y8ubRR9Bo6TpMdO4+2U+R58Z5bAgMBAAE=
-----END PUBLIC KEY-----

Display DER Format of Certificate

!openssl x509 -in cert.der -inform DER -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 16322561221100825744 (0xe28562f33047ec90)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=California, L=Sausalito, O=HealtheData1, CN=Eric Haas, DVM/emailAddress=ehaas@healthedata1.org
        Validity
            Not Before: Oct 27 17:42:04 2021 GMT
            Not After : Oct 22 17:42:04 2022 GMT
        Subject: C=US, ST=California, L=Sausalito, O=HealtheData1, CN=Eric Haas, DVM/emailAddress=ehaas@healthedata1.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (3072 bit)
                Modulus:
                    00:e9:29:c4:a4:a0:03:3a:b1:6d:b5:fa:f5:93:78:
                    85:4e:1b:1d:74:31:66:ac:43:2e:d5:1e:27:7a:cf:
                    2a:97:00:13:3e:ce:77:c1:9d:77:a7:c9:4d:23:d4:
                    54:ef:3e:64:5f:38:3a:0e:0d:75:6e:3d:4c:03:a2:
                    68:42:1e:1f:9b:72:55:bd:28:cd:a8:e7:ad:dc:c5:
                    12:84:bc:19:da:1e:bb:23:c3:9c:8d:9b:13:b9:62:
                    31:11:6e:1b:47:75:07:aa:92:d7:70:dd:56:04:45:
                    1f:47:a3:12:ce:dc:59:2c:cd:1d:bd:d8:74:c1:e3:
                    15:b7:c9:69:55:de:04:e4:31:0a:33:3d:27:09:28:
                    7d:db:1b:c3:ea:ab:a0:18:3c:9e:c2:e1:80:48:95:
                    44:9d:04:c5:65:de:a9:de:11:f8:3b:7e:ec:62:15:
                    f5:e8:7d:d4:d5:9b:ba:cc:8a:21:bb:5f:dc:f8:dc:
                    f7:e2:96:a6:9e:8a:c7:e6:b5:1c:40:95:1c:04:35:
                    7a:c7:dc:eb:ce:0c:fc:8b:d2:b4:e7:1b:c0:ac:6c:
                    05:d8:54:83:26:75:d1:de:e1:91:7d:a6:58:7d:e6:
                    c8:f8:a4:c4:e1:2e:d7:09:5f:fa:3d:5c:72:e3:87:
                    b1:26:67:28:09:1d:61:10:ab:83:f0:17:06:5d:99:
                    b7:1f:84:29:8e:af:cf:07:f0:16:d4:ae:ef:fb:57:
                    ac:db:b0:6d:4d:d4:25:a5:9d:d9:5b:57:39:cb:fb:
                    72:0c:3a:bf:24:5d:a1:dc:6a:7a:9f:72:48:05:57:
                    81:2b:9d:f8:c5:26:ad:4e:21:a0:26:b0:c8:fc:e7:
                    13:24:8f:25:cb:c9:c2:cb:fe:ee:67:da:8e:81:83:
                    dd:ff:51:17:ea:b8:ea:12:20:63:82:47:6e:43:69:
                    9c:e9:c3:69:37:b3:ba:04:f5:d3:30:11:65:ed:7d:
                    78:18:b6:1d:9b:96:3c:b9:b4:51:f4:1a:3a:4e:93:
                    1d:3b:8f:b6:53:e4:79:f1:9e:5b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:TRUE
            X509v3 Key Usage:
                Digital Signature, Non Repudiation, Key Encipherment
            X509v3 Subject Alternative Name:
                DNS:www.healthedatainc.com
    Signature Algorithm: sha256WithRSAEncryption
         0a:e3:14:36:af:5a:cb:ef:9e:e5:80:bb:40:53:bd:e2:b7:f3:
         c4:64:27:17:71:a4:95:93:40:5e:62:2b:ec:f5:46:76:21:98:
         d8:64:f1:ff:d4:6d:1c:2c:13:2b:39:0b:fb:2b:ca:a2:37:99:
         f5:d5:4a:f3:0a:c6:56:24:64:3b:f8:d4:c9:9f:8a:71:64:68:
         91:48:ec:e7:d9:dc:24:64:4c:49:24:71:b5:e0:90:ba:3b:78:
         85:fd:be:fe:a5:7e:7f:6b:0c:42:d1:2b:c0:f1:37:d2:f6:ea:
         45:85:0d:34:d0:55:11:0a:5a:43:1f:da:70:90:d4:bf:db:fb:
         b2:31:e2:24:3a:97:c1:0f:ab:ce:3f:79:88:70:08:69:e1:07:
         a8:5f:cc:b3:6d:a9:d9:b7:99:ba:ab:c8:40:a7:8c:97:eb:2f:
         56:a1:ed:8a:ec:d6:9c:5e:14:e9:52:26:a6:4a:f8:6c:79:7b:
         7a:05:7d:1f:a9:7a:4f:64:8c:92:3e:aa:0d:4e:5e:f4:d8:34:
         66:52:d3:b6:8b:2c:84:12:e6:a2:91:7b:7b:b1:f2:ad:31:00:
         99:f3:8f:29:07:b4:12:0f:44:da:ea:f3:c6:a0:6e:1b:9e:24:
         e7:41:b6:27:41:62:3e:c4:09:02:11:62:34:6c:12:83:84:b7:
         1f:25:43:1b:bb:9e:29:dd:0b:f7:7c:0a:38:d3:98:1a:f3:0a:
         8b:30:87:07:ea:4a:bc:43:09:a1:9f:32:f6:e8:81:f8:71:57:
         72:9f:51:68:9a:89:f7:b1:e9:65:31:2f:fa:58:82:a7:63:d2:
         5f:37:0a:d2:bd:b9:49:cc:a3:17:a7:4b:a2:e2:b6:48:1d:f4:
         c4:0b:c9:f2:d1:39:f3:8f:a3:a9:0e:82:19:e7:14:f5:78:ef:
         94:08:ee:25:d0:ab:0d:e5:4f:b2:39:27:ec:e3:d7:1b:ef:17:
         65:4e:f3:a8:76:4d:99:75:f5:61:8e:07:e6:b8:04:27:8f:25:
         ad:4a:cf:dc:8f:11
-----BEGIN CERTIFICATE-----
MIIE3zCCA0egAwIBAgIJAOKFYvMwR+yQMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD
VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU2F1c2FsaXRv
MRUwEwYDVQQKDAxIZWFsdGhlRGF0YTExFzAVBgNVBAMMDkVyaWMgSGFhcywgRFZN
MSUwIwYJKoZIhvcNAQkBFhZlaGFhc0BoZWFsdGhlZGF0YTEub3JnMB4XDTIxMTAy
NzE3NDIwNFoXDTIyMTAyMjE3NDIwNFowgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQI
DApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYXVzYWxpdG8xFTATBgNVBAoMDEhlYWx0
aGVEYXRhMTEXMBUGA1UEAwwORXJpYyBIYWFzLCBEVk0xJTAjBgkqhkiG9w0BCQEW
FmVoYWFzQGhlYWx0aGVkYXRhMS5vcmcwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
ggGKAoIBgQDpKcSkoAM6sW21+vWTeIVOGx10MWasQy7VHid6zyqXABM+znfBnXen
yU0j1FTvPmRfODoODXVuPUwDomhCHh+bclW9KM2o563cxRKEvBnaHrsjw5yNmxO5
YjERbhtHdQeqktdw3VYERR9HoxLO3FkszR292HTB4xW3yWlV3gTkMQozPScJKH3b
G8Pqq6AYPJ7C4YBIlUSdBMVl3qneEfg7fuxiFfXofdTVm7rMiiG7X9z43Pfilqae
isfmtRxAlRwENXrH3OvODPyL0rTnG8CsbAXYVIMmddHe4ZF9plh95sj4pMThLtcJ
X/o9XHLjh7EmZygJHWEQq4PwFwZdmbcfhCmOr88H8BbUru/7V6zbsG1N1CWlndlb
VznL+3IMOr8kXaHcanqfckgFV4ErnfjFJq1OIaAmsMj85xMkjyXLycLL/u5n2o6B
g93/URfquOoSIGOCR25DaZzpw2k3s7oE9dMwEWXtfXgYth2bljy5tFH0GjpOkx07
j7ZT5HnxnlsCAwEAAaNAMD4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCBeAwIQYD
VR0RBBowGIIWd3d3LmhlYWx0aGVkYXRhaW5jLmNvbTANBgkqhkiG9w0BAQsFAAOC
AYEACuMUNq9ay++e5YC7QFO94rfzxGQnF3GklZNAXmIr7PVGdiGY2GTx/9RtHCwT
KzkL+yvKojeZ9dVK8wrGViRkO/jUyZ+KcWRokUjs59ncJGRMSSRxteCQujt4hf2+
/qV+f2sMQtErwPE30vbqRYUNNNBVEQpaQx/acJDUv9v7sjHiJDqXwQ+rzj95iHAI
aeEHqF/Ms22p2beZuqvIQKeMl+svVqHtiuzWnF4U6VImpkr4bHl7egV9H6l6T2SM
kj6qDU5e9Ng0ZlLTtosshBLmopF7e7HyrTEAmfOPKQe0Eg9E2urzxqBuG54k50G2
J0FiPsQJAhFiNGwSg4S3HyVDG7ueKd0L93wKONOYGvMKizCHB+pKvEMJoZ8y9uiB
+HFXcp9RaJqJ97HpZTEv+liCp2PSXzcK0r25ScyjF6dLouK2SB30xAvJ8tE584+j
qQ6CGecU9XjvlAjuJdCrDeVPsjkn7OPXG+8XZU7zqHZNmXX1YY4H5rgEJ48lrUrP
3I8R
-----END CERTIFICATE-----

Display PEM Format of Certificate

!openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem
!cat cert.pem
-----BEGIN CERTIFICATE-----
MIIE3zCCA0egAwIBAgIJAOKFYvMwR+yQMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD
VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU2F1c2FsaXRv
MRUwEwYDVQQKDAxIZWFsdGhlRGF0YTExFzAVBgNVBAMMDkVyaWMgSGFhcywgRFZN
MSUwIwYJKoZIhvcNAQkBFhZlaGFhc0BoZWFsdGhlZGF0YTEub3JnMB4XDTIxMTAy
NzE3NDIwNFoXDTIyMTAyMjE3NDIwNFowgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQI
DApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYXVzYWxpdG8xFTATBgNVBAoMDEhlYWx0
aGVEYXRhMTEXMBUGA1UEAwwORXJpYyBIYWFzLCBEVk0xJTAjBgkqhkiG9w0BCQEW
FmVoYWFzQGhlYWx0aGVkYXRhMS5vcmcwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
ggGKAoIBgQDpKcSkoAM6sW21+vWTeIVOGx10MWasQy7VHid6zyqXABM+znfBnXen
yU0j1FTvPmRfODoODXVuPUwDomhCHh+bclW9KM2o563cxRKEvBnaHrsjw5yNmxO5
YjERbhtHdQeqktdw3VYERR9HoxLO3FkszR292HTB4xW3yWlV3gTkMQozPScJKH3b
G8Pqq6AYPJ7C4YBIlUSdBMVl3qneEfg7fuxiFfXofdTVm7rMiiG7X9z43Pfilqae
isfmtRxAlRwENXrH3OvODPyL0rTnG8CsbAXYVIMmddHe4ZF9plh95sj4pMThLtcJ
X/o9XHLjh7EmZygJHWEQq4PwFwZdmbcfhCmOr88H8BbUru/7V6zbsG1N1CWlndlb
VznL+3IMOr8kXaHcanqfckgFV4ErnfjFJq1OIaAmsMj85xMkjyXLycLL/u5n2o6B
g93/URfquOoSIGOCR25DaZzpw2k3s7oE9dMwEWXtfXgYth2bljy5tFH0GjpOkx07
j7ZT5HnxnlsCAwEAAaNAMD4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCBeAwIQYD
VR0RBBowGIIWd3d3LmhlYWx0aGVkYXRhaW5jLmNvbTANBgkqhkiG9w0BAQsFAAOC
AYEACuMUNq9ay++e5YC7QFO94rfzxGQnF3GklZNAXmIr7PVGdiGY2GTx/9RtHCwT
KzkL+yvKojeZ9dVK8wrGViRkO/jUyZ+KcWRokUjs59ncJGRMSSRxteCQujt4hf2+
/qV+f2sMQtErwPE30vbqRYUNNNBVEQpaQx/acJDUv9v7sjHiJDqXwQ+rzj95iHAI
aeEHqF/Ms22p2beZuqvIQKeMl+svVqHtiuzWnF4U6VImpkr4bHl7egV9H6l6T2SM
kj6qDU5e9Ng0ZlLTtosshBLmopF7e7HyrTEAmfOPKQe0Eg9E2urzxqBuG54k50G2
J0FiPsQJAhFiNGwSg4S3HyVDG7ueKd0L93wKONOYGvMKizCHB+pKvEMJoZ8y9uiB
+HFXcp9RaJqJ97HpZTEv+liCp2PSXzcK0r25ScyjF6dLouK2SB30xAvJ8tE584+j
qQ6CGecU9XjvlAjuJdCrDeVPsjkn7OPXG+8XZU7zqHZNmXX1YY4H5rgEJ48lrUrP
3I8R
-----END CERTIFICATE-----

3. Create JWS to Attach to Bundle

3.1. Prepare Header

note the base64 DER is Cert PEM file wihout the footer and header and line returns

with open('cert.pem') as f:
    der = (f.read())  # base64 DER is PEM wihout the footer and header and line returns
der = der.replace('-----BEGIN CERTIFICATE-----','')
der = der.replace('-----END CERTIFICATE-----','')
der = der.replace('\n','')
header = {"alg": "RS256","kty": "RS", "x5c": [der]}
header
{'alg': 'RS256',
 'kty': 'RS',
 'x5c': ['MIIE3zCCA0egAwIBAgIJAOKFYvMwR+yQMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU2F1c2FsaXRvMRUwEwYDVQQKDAxIZWFsdGhlRGF0YTExFzAVBgNVBAMMDkVyaWMgSGFhcywgRFZNMSUwIwYJKoZIhvcNAQkBFhZlaGFhc0BoZWFsdGhlZGF0YTEub3JnMB4XDTIxMTAyNzE3NDIwNFoXDTIyMTAyMjE3NDIwNFowgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYXVzYWxpdG8xFTATBgNVBAoMDEhlYWx0aGVEYXRhMTEXMBUGA1UEAwwORXJpYyBIYWFzLCBEVk0xJTAjBgkqhkiG9w0BCQEWFmVoYWFzQGhlYWx0aGVkYXRhMS5vcmcwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDpKcSkoAM6sW21+vWTeIVOGx10MWasQy7VHid6zyqXABM+znfBnXenyU0j1FTvPmRfODoODXVuPUwDomhCHh+bclW9KM2o563cxRKEvBnaHrsjw5yNmxO5YjERbhtHdQeqktdw3VYERR9HoxLO3FkszR292HTB4xW3yWlV3gTkMQozPScJKH3bG8Pqq6AYPJ7C4YBIlUSdBMVl3qneEfg7fuxiFfXofdTVm7rMiiG7X9z43PfilqaeisfmtRxAlRwENXrH3OvODPyL0rTnG8CsbAXYVIMmddHe4ZF9plh95sj4pMThLtcJX/o9XHLjh7EmZygJHWEQq4PwFwZdmbcfhCmOr88H8BbUru/7V6zbsG1N1CWlndlbVznL+3IMOr8kXaHcanqfckgFV4ErnfjFJq1OIaAmsMj85xMkjyXLycLL/u5n2o6Bg93/URfquOoSIGOCR25DaZzpw2k3s7oE9dMwEWXtfXgYth2bljy5tFH0GjpOkx07j7ZT5HnxnlsCAwEAAaNAMD4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCBeAwIQYDVR0RBBowGIIWd3d3LmhlYWx0aGVkYXRhaW5jLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEACuMUNq9ay++e5YC7QFO94rfzxGQnF3GklZNAXmIr7PVGdiGY2GTx/9RtHCwTKzkL+yvKojeZ9dVK8wrGViRkO/jUyZ+KcWRokUjs59ncJGRMSSRxteCQujt4hf2+/qV+f2sMQtErwPE30vbqRYUNNNBVEQpaQx/acJDUv9v7sjHiJDqXwQ+rzj95iHAIaeEHqF/Ms22p2beZuqvIQKeMl+svVqHtiuzWnF4U6VImpkr4bHl7egV9H6l6T2SMkj6qDU5e9Ng0ZlLTtosshBLmopF7e7HyrTEAmfOPKQe0Eg9E2urzxqBuG54k50G2J0FiPsQJAhFiNGwSg4S3HyVDG7ueKd0L93wKONOYGvMKizCHB+pKvEMJoZ8y9uiB+HFXcp9RaJqJ97HpZTEv+liCp2PSXzcK0r25ScyjF6dLouK2SB30xAvJ8tE584+jqQ6CGecU9XjvlAjuJdCrDeVPsjkn7OPXG+8XZU7zqHZNmXX1YY4H5rgEJ48lrUrP3I8R']}

3.2. Prepare Payload

The payload is the base64_url form of the canonicalized version of the searchset Bundle before attaching the signature

Canonicalize the bundle using IETF JSON Canonicalization Scheme (JCS) before adding the signature element:

  • Remove the id and meta elements if present before canonicalization
  • The base64_url of the payload entry is combined with 3.3 below using the jws.sign method.
from jcs import canonicalize #package for a JCS (RFC 8785) compliant canonicalizer.
from json import loads
searchset_bundle = r'''{
  "resourceType": "Bundle",
  "id": "d88e2910-3c95-469d-b1f4-ab5553b471fb",
  "meta": {
    "lastUpdated": "2020-10-23T04:54:56.048+00:00"
  },
  "type": "searchset",
  "total": 1,
  "link": [ {
    "relation": "self",
    "url": "http://hapi.fhir.org/baseR4/Condition?patient=06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b&clinical-status=active,recurrance,remission"
  } ],
  "entry": [ {
    "fullUrl": "http://hapi.fhir.org/baseR4/Condition/4ac41715-fcbd-421c-8796-9b2c9706dd3f",
    "resource": {
      "resourceType": "Condition",
      "id": "4ac41715-fcbd-421c-8796-9b2c9706dd3f",
      "meta": {
        "versionId": "10",
        "lastUpdated": "2020-04-28T20:28:00.008+00:00",
        "source": "#cabiJIK51sD2iz4N",
        "profile": [ "http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition" ]
      },
      "clinicalStatus": {
        "coding": [ {
          "system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
          "code": "active"
        } ]
      },
      "verificationStatus": {
        "coding": [ {
          "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status",
          "code": "confirmed"
        } ]
      },
      "category": [ {
        "coding": [ {
          "system": "http://terminology.hl7.org/CodeSystem/condition-category",
          "code": "encounter-diagnosis",
          "display": "Encounter Diagnosis"
        } ]
      } ],
      "code": {
        "coding": [ {
          "system": "http://snomed.info/sct",
          "code": "1234",
          "display": "Examplitis"
        } ],
        "text": "Examplitis"
      },
      "subject": {
        "reference": "https://example.org/Patient/06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b"
      },
      "encounter": {
        "reference": "https://example.org/Encounter/5fe62cd5-bfcf-4d3b-a1e9-80d6f75d6f82"
      },
      "onsetDateTime": "2018-10-21T21:22:15-07:00",
      "recordedDate": "2018-10-21T21:22:15-07:00"
    },
    "search": {
      "mode": "match"
    }
  } ]
}
'''
searchset_bundle = loads(searchset_bundle) #convert to Python object
searchset_bundle_id = searchset_bundle.pop("id") # remove id
searchset_bundle_meta = searchset_bundle.pop("meta") # remove meta
payload = canonicalize(searchset_bundle)
payload
b'{"entry":[{"fullUrl":"http://hapi.fhir.org/baseR4/Condition/4ac41715-fcbd-421c-8796-9b2c9706dd3f","resource":{"category":[{"coding":[{"code":"encounter-diagnosis","display":"Encounter Diagnosis","system":"http://terminology.hl7.org/CodeSystem/condition-category"}]}],"clinicalStatus":{"coding":[{"code":"active","system":"http://terminology.hl7.org/CodeSystem/condition-clinical"}]},"code":{"coding":[{"code":"1234","display":"Examplitis","system":"http://snomed.info/sct"}],"text":"Examplitis"},"encounter":{"reference":"https://example.org/Encounter/5fe62cd5-bfcf-4d3b-a1e9-80d6f75d6f82"},"id":"4ac41715-fcbd-421c-8796-9b2c9706dd3f","meta":{"lastUpdated":"2020-04-28T20:28:00.008+00:00","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition"],"source":"#cabiJIK51sD2iz4N","versionId":"10"},"onsetDateTime":"2018-10-21T21:22:15-07:00","recordedDate":"2018-10-21T21:22:15-07:00","resourceType":"Condition","subject":{"reference":"https://example.org/Patient/06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b"},"verificationStatus":{"coding":[{"code":"confirmed","system":"http://terminology.hl7.org/CodeSystem/condition-ver-status"}]}},"search":{"mode":"match"}}],"link":[{"relation":"self","url":"http://hapi.fhir.org/baseR4/Condition?patient=06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b&clinical-status=active,recurrance,remission"}],"resourceType":"Bundle","total":1,"type":"searchset"}'

3.3 Create Signature using private key and the RS256 algorithm to get the JWS compact serialization format

note the signature is displayed with the parts labeled and separated with line breaks for easier viewing.

from jose import jws
with open('private-key.pem') as f:
    private_key = (f.read())

signature = jws.sign(payload,private_key,algorithm='RS256',headers=header)

labels = ['header', 'payload', 'signature']
for i,j in enumerate(signature.split('.')):
    print(f'{labels[i]}:')
    print(f'{j}')
    print()
header:
eyJhbGciOiJSUzI1NiIsImt0eSI6IlJTIiwidHlwIjoiSldUIiwieDVjIjpbIk1JSUUzekNDQTBlZ0F3SUJBZ0lKQU9LRll2TXdSK3lRTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdOTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKVTJGMWMyRnNhWFJ2TVJVd0V3WURWUVFLREF4SVpXRnNkR2hsUkdGMFlURXhGekFWQmdOVkJBTU1Ea1Z5YVdNZ1NHRmhjeXdnUkZaTk1TVXdJd1lKS29aSWh2Y05BUWtCRmhabGFHRmhjMEJvWldGc2RHaGxaR0YwWVRFdWIzSm5NQjRYRFRJeE1UQXlOekUzTkRJd05Gb1hEVEl5TVRBeU1qRTNOREl3TkZvd2dZMHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbFRZWFZ6WVd4cGRHOHhGVEFUQmdOVkJBb01ERWhsWVd4MGFHVkVZWFJoTVRFWE1CVUdBMVVFQXd3T1JYSnBZeUJJWVdGekxDQkVWazB4SlRBakJna3Foa2lHOXcwQkNRRVdGbVZvWVdGelFHaGxZV3gwYUdWa1lYUmhNUzV2Y21jd2dnR2lNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUURwS2NTa29BTTZzVzIxK3ZXVGVJVk9HeDEwTVdhc1F5N1ZIaWQ2enlxWEFCTSt6bmZCblhlbnlVMGoxRlR2UG1SZk9Eb09EWFZ1UFV3RG9taENIaCtiY2xXOUtNMm81NjNjeFJLRXZCbmFIcnNqdzV5Tm14TzVZakVSYmh0SGRRZXFrdGR3M1ZZRVJSOUhveExPM0Zrc3pSMjkySFRCNHhXM3lXbFYzZ1RrTVFvelBTY0pLSDNiRzhQcXE2QVlQSjdDNFlCSWxVU2RCTVZsM3FuZUVmZzdmdXhpRmZYb2ZkVFZtN3JNaWlHN1g5ejQzUGZpbHFhZWlzZm10UnhBbFJ3RU5YckgzT3ZPRFB5TDByVG5HOENzYkFYWVZJTW1kZEhlNFpGOXBsaDk1c2o0cE1UaEx0Y0pYL285WEhMamg3RW1aeWdKSFdFUXE0UHdGd1pkbWJjZmhDbU9yODhIOEJiVXJ1LzdWNnpic0cxTjFDV2xuZGxiVnpuTCszSU1PcjhrWGFIY2FucWZja2dGVjRFcm5makZKcTFPSWFBbXNNajg1eE1ranlYTHljTEwvdTVuMm82Qmc5My9VUmZxdU9vU0lHT0NSMjVEYVp6cHcyazNzN29FOWRNd0VXWHRmWGdZdGgyYmxqeTV0RkgwR2pwT2t4MDdqN1pUNUhueG5sc0NBd0VBQWFOQU1ENHdEQVlEVlIwVEJBVXdBd0VCL3pBTEJnTlZIUThFQkFNQ0JlQXdJUVlEVlIwUkJCb3dHSUlXZDNkM0xtaGxZV3gwYUdWa1lYUmhhVzVqTG1OdmJUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FZRUFDdU1VTnE5YXkrK2U1WUM3UUZPOTRyZnp4R1FuRjNHa2xaTkFYbUlyN1BWR2RpR1kyR1R4LzlSdEhDd1RLemtMK3l2S29qZVo5ZFZLOHdyR1ZpUmtPL2pVeVorS2NXUm9rVWpzNTluY0pHUk1TU1J4dGVDUXVqdDRoZjIrL3FWK2Yyc01RdEVyd1BFMzB2YnFSWVVOTk5CVkVRcGFReC9hY0pEVXY5djdzakhpSkRxWHdRK3J6ajk1aUhBSWFlRUhxRi9NczIycDJiZVp1cXZJUUtlTWwrc3ZWcUh0aXV6V25GNFU2VkltcGtyNGJIbDdlZ1Y5SDZsNlQyU01rajZxRFU1ZTlOZzBabExUdG9zc2hCTG1vcEY3ZTdIeXJURUFtZk9QS1FlMEVnOUUydXJ6eHFCdUc1NGs1MEcySjBGaVBzUUpBaEZpTkd3U2c0UzNIeVZERzd1ZUtkMEw5M3dLT05PWUd2TUtpekNIQitwS3ZFTUpvWjh5OXVpQitIRlhjcDlSYUpxSjk3SHBaVEV2K2xpQ3AyUFNYemNLMHIyNVNjeWpGNmRMb3VLMlNCMzB4QXZKOHRFNTg0K2pxUTZDR2VjVTlYanZsQWp1SmRDckRlVlBzamtuN09QWEcrOFhaVTd6cUhaTm1YWDFZWTRINXJnRUo0OGxyVXJQM0k4UiJdfQ

payload:
eyJlbnRyeSI6W3siZnVsbFVybCI6Imh0dHA6Ly9oYXBpLmZoaXIub3JnL2Jhc2VSNC9Db25kaXRpb24vNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwicmVzb3VyY2UiOnsiY2F0ZWdvcnkiOlt7ImNvZGluZyI6W3siY29kZSI6ImVuY291bnRlci1kaWFnbm9zaXMiLCJkaXNwbGF5IjoiRW5jb3VudGVyIERpYWdub3NpcyIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNhdGVnb3J5In1dfV0sImNsaW5pY2FsU3RhdHVzIjp7ImNvZGluZyI6W3siY29kZSI6ImFjdGl2ZSIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsIn1dfSwiY29kZSI6eyJjb2RpbmciOlt7ImNvZGUiOiIxMjM0IiwiZGlzcGxheSI6IkV4YW1wbGl0aXMiLCJzeXN0ZW0iOiJodHRwOi8vc25vbWVkLmluZm8vc2N0In1dLCJ0ZXh0IjoiRXhhbXBsaXRpcyJ9LCJlbmNvdW50ZXIiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9FbmNvdW50ZXIvNWZlNjJjZDUtYmZjZi00ZDNiLWExZTktODBkNmY3NWQ2ZjgyIn0sImlkIjoiNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwibWV0YSI6eyJsYXN0VXBkYXRlZCI6IjIwMjAtMDQtMjhUMjA6Mjg6MDAuMDA4KzAwOjAwIiwicHJvZmlsZSI6WyJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWNvbmRpdGlvbiJdLCJzb3VyY2UiOiIjY2FiaUpJSzUxc0QyaXo0TiIsInZlcnNpb25JZCI6IjEwIn0sIm9uc2V0RGF0ZVRpbWUiOiIyMDE4LTEwLTIxVDIxOjIyOjE1LTA3OjAwIiwicmVjb3JkZWREYXRlIjoiMjAxOC0xMC0yMVQyMToyMjoxNS0wNzowMCIsInJlc291cmNlVHlwZSI6IkNvbmRpdGlvbiIsInN1YmplY3QiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9QYXRpZW50LzA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiJ9LCJ2ZXJpZmljYXRpb25TdGF0dXMiOnsiY29kaW5nIjpbeyJjb2RlIjoiY29uZmlybWVkIiwic3lzdGVtIjoiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tdmVyLXN0YXR1cyJ9XX19LCJzZWFyY2giOnsibW9kZSI6Im1hdGNoIn19XSwibGluayI6W3sicmVsYXRpb24iOiJzZWxmIiwidXJsIjoiaHR0cDovL2hhcGkuZmhpci5vcmcvYmFzZVI0L0NvbmRpdGlvbj9wYXRpZW50PTA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiZjbGluaWNhbC1zdGF0dXM9YWN0aXZlLHJlY3VycmFuY2UscmVtaXNzaW9uIn1dLCJyZXNvdXJjZVR5cGUiOiJCdW5kbGUiLCJ0b3RhbCI6MSwidHlwZSI6InNlYXJjaHNldCJ9

signature:
rE5gfhbdbkmuYpKPb87Tpg1nRdRJTyNfWAOmXR2AA63FGKz2br_aAFtXj2UwIVHjp_AmgOZrpKmUFZnibBRcRTBUVfPWVrgRHleP1Dd1QL2qiaz0Pxw38odIVKQw88pFXBfIASsnrFMEJ_x07XQLWaXLOcEYVBb2s1SoULIW_McJCeCVKEnhrrsmNhXvAHLxYm2y8lUdTYYMH7rCM2FEIglavxLA4QI1nPv5i5ZwSOZDI1OoPtK4dHbbvggsKnAtBJL890AJ7JD9h1QTmw8vSWjydCBNilgGiyffPmfSGXFvGQ9tEcETHW0NlUEF649Zt-k71aMhkjooCsRwM9Dmb4STqO7Fy0MzWbvdi6K46W_rf77gv75Xr_cQyvSubWumRTMompgG1qOz5LbClfKHoWWyIzBxM2u7wqBukJTAIm10DSDgFg79-5oLPIDOj19VYsnLcyYFdYIx2G3gkBhcbExDdytlKGIZ8YvIEojDfNibC3wLWwNvmptBww-hPdT7

3.4. Create detached payload by removing the payload from the JWS

note the signature is displayed with the parts labeled and separated with line breaks for easier viewing then as compact serialization format

split_sig = signature.split('.')
split_sig[1] = ''
signature = '.'.join(split_sig)
for i,j in enumerate(signature.split('.')):
    print(f'{labels[i]}:')
    print(f'{j}')
    print()
print(f'\nSignature in compact serialization format:\n{"="*80}\n{signature}')
header:
eyJhbGciOiJSUzI1NiIsImt0eSI6IlJTIiwidHlwIjoiSldUIiwieDVjIjpbIk1JSUUzekNDQTBlZ0F3SUJBZ0lKQU9LRll2TXdSK3lRTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdOTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKVTJGMWMyRnNhWFJ2TVJVd0V3WURWUVFLREF4SVpXRnNkR2hsUkdGMFlURXhGekFWQmdOVkJBTU1Ea1Z5YVdNZ1NHRmhjeXdnUkZaTk1TVXdJd1lKS29aSWh2Y05BUWtCRmhabGFHRmhjMEJvWldGc2RHaGxaR0YwWVRFdWIzSm5NQjRYRFRJeE1UQXlOekUzTkRJd05Gb1hEVEl5TVRBeU1qRTNOREl3TkZvd2dZMHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbFRZWFZ6WVd4cGRHOHhGVEFUQmdOVkJBb01ERWhsWVd4MGFHVkVZWFJoTVRFWE1CVUdBMVVFQXd3T1JYSnBZeUJJWVdGekxDQkVWazB4SlRBakJna3Foa2lHOXcwQkNRRVdGbVZvWVdGelFHaGxZV3gwYUdWa1lYUmhNUzV2Y21jd2dnR2lNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUURwS2NTa29BTTZzVzIxK3ZXVGVJVk9HeDEwTVdhc1F5N1ZIaWQ2enlxWEFCTSt6bmZCblhlbnlVMGoxRlR2UG1SZk9Eb09EWFZ1UFV3RG9taENIaCtiY2xXOUtNMm81NjNjeFJLRXZCbmFIcnNqdzV5Tm14TzVZakVSYmh0SGRRZXFrdGR3M1ZZRVJSOUhveExPM0Zrc3pSMjkySFRCNHhXM3lXbFYzZ1RrTVFvelBTY0pLSDNiRzhQcXE2QVlQSjdDNFlCSWxVU2RCTVZsM3FuZUVmZzdmdXhpRmZYb2ZkVFZtN3JNaWlHN1g5ejQzUGZpbHFhZWlzZm10UnhBbFJ3RU5YckgzT3ZPRFB5TDByVG5HOENzYkFYWVZJTW1kZEhlNFpGOXBsaDk1c2o0cE1UaEx0Y0pYL285WEhMamg3RW1aeWdKSFdFUXE0UHdGd1pkbWJjZmhDbU9yODhIOEJiVXJ1LzdWNnpic0cxTjFDV2xuZGxiVnpuTCszSU1PcjhrWGFIY2FucWZja2dGVjRFcm5makZKcTFPSWFBbXNNajg1eE1ranlYTHljTEwvdTVuMm82Qmc5My9VUmZxdU9vU0lHT0NSMjVEYVp6cHcyazNzN29FOWRNd0VXWHRmWGdZdGgyYmxqeTV0RkgwR2pwT2t4MDdqN1pUNUhueG5sc0NBd0VBQWFOQU1ENHdEQVlEVlIwVEJBVXdBd0VCL3pBTEJnTlZIUThFQkFNQ0JlQXdJUVlEVlIwUkJCb3dHSUlXZDNkM0xtaGxZV3gwYUdWa1lYUmhhVzVqTG1OdmJUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FZRUFDdU1VTnE5YXkrK2U1WUM3UUZPOTRyZnp4R1FuRjNHa2xaTkFYbUlyN1BWR2RpR1kyR1R4LzlSdEhDd1RLemtMK3l2S29qZVo5ZFZLOHdyR1ZpUmtPL2pVeVorS2NXUm9rVWpzNTluY0pHUk1TU1J4dGVDUXVqdDRoZjIrL3FWK2Yyc01RdEVyd1BFMzB2YnFSWVVOTk5CVkVRcGFReC9hY0pEVXY5djdzakhpSkRxWHdRK3J6ajk1aUhBSWFlRUhxRi9NczIycDJiZVp1cXZJUUtlTWwrc3ZWcUh0aXV6V25GNFU2VkltcGtyNGJIbDdlZ1Y5SDZsNlQyU01rajZxRFU1ZTlOZzBabExUdG9zc2hCTG1vcEY3ZTdIeXJURUFtZk9QS1FlMEVnOUUydXJ6eHFCdUc1NGs1MEcySjBGaVBzUUpBaEZpTkd3U2c0UzNIeVZERzd1ZUtkMEw5M3dLT05PWUd2TUtpekNIQitwS3ZFTUpvWjh5OXVpQitIRlhjcDlSYUpxSjk3SHBaVEV2K2xpQ3AyUFNYemNLMHIyNVNjeWpGNmRMb3VLMlNCMzB4QXZKOHRFNTg0K2pxUTZDR2VjVTlYanZsQWp1SmRDckRlVlBzamtuN09QWEcrOFhaVTd6cUhaTm1YWDFZWTRINXJnRUo0OGxyVXJQM0k4UiJdfQ

payload:


signature:
rE5gfhbdbkmuYpKPb87Tpg1nRdRJTyNfWAOmXR2AA63FGKz2br_aAFtXj2UwIVHjp_AmgOZrpKmUFZnibBRcRTBUVfPWVrgRHleP1Dd1QL2qiaz0Pxw38odIVKQw88pFXBfIASsnrFMEJ_x07XQLWaXLOcEYVBb2s1SoULIW_McJCeCVKEnhrrsmNhXvAHLxYm2y8lUdTYYMH7rCM2FEIglavxLA4QI1nPv5i5ZwSOZDI1OoPtK4dHbbvggsKnAtBJL890AJ7JD9h1QTmw8vSWjydCBNilgGiyffPmfSGXFvGQ9tEcETHW0NlUEF649Zt-k71aMhkjooCsRwM9Dmb4STqO7Fy0MzWbvdi6K46W_rf77gv75Xr_cQyvSubWumRTMompgG1qOz5LbClfKHoWWyIzBxM2u7wqBukJTAIm10DSDgFg79-5oLPIDOj19VYsnLcyYFdYIx2G3gkBhcbExDdytlKGIZ8YvIEojDfNibC3wLWwNvmptBww-hPdT7


Signature in compact serialization format:
================================================================================
eyJhbGciOiJSUzI1NiIsImt0eSI6IlJTIiwidHlwIjoiSldUIiwieDVjIjpbIk1JSUUzekNDQTBlZ0F3SUJBZ0lKQU9LRll2TXdSK3lRTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdOTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKVTJGMWMyRnNhWFJ2TVJVd0V3WURWUVFLREF4SVpXRnNkR2hsUkdGMFlURXhGekFWQmdOVkJBTU1Ea1Z5YVdNZ1NHRmhjeXdnUkZaTk1TVXdJd1lKS29aSWh2Y05BUWtCRmhabGFHRmhjMEJvWldGc2RHaGxaR0YwWVRFdWIzSm5NQjRYRFRJeE1UQXlOekUzTkRJd05Gb1hEVEl5TVRBeU1qRTNOREl3TkZvd2dZMHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbFRZWFZ6WVd4cGRHOHhGVEFUQmdOVkJBb01ERWhsWVd4MGFHVkVZWFJoTVRFWE1CVUdBMVVFQXd3T1JYSnBZeUJJWVdGekxDQkVWazB4SlRBakJna3Foa2lHOXcwQkNRRVdGbVZvWVdGelFHaGxZV3gwYUdWa1lYUmhNUzV2Y21jd2dnR2lNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUURwS2NTa29BTTZzVzIxK3ZXVGVJVk9HeDEwTVdhc1F5N1ZIaWQ2enlxWEFCTSt6bmZCblhlbnlVMGoxRlR2UG1SZk9Eb09EWFZ1UFV3RG9taENIaCtiY2xXOUtNMm81NjNjeFJLRXZCbmFIcnNqdzV5Tm14TzVZakVSYmh0SGRRZXFrdGR3M1ZZRVJSOUhveExPM0Zrc3pSMjkySFRCNHhXM3lXbFYzZ1RrTVFvelBTY0pLSDNiRzhQcXE2QVlQSjdDNFlCSWxVU2RCTVZsM3FuZUVmZzdmdXhpRmZYb2ZkVFZtN3JNaWlHN1g5ejQzUGZpbHFhZWlzZm10UnhBbFJ3RU5YckgzT3ZPRFB5TDByVG5HOENzYkFYWVZJTW1kZEhlNFpGOXBsaDk1c2o0cE1UaEx0Y0pYL285WEhMamg3RW1aeWdKSFdFUXE0UHdGd1pkbWJjZmhDbU9yODhIOEJiVXJ1LzdWNnpic0cxTjFDV2xuZGxiVnpuTCszSU1PcjhrWGFIY2FucWZja2dGVjRFcm5makZKcTFPSWFBbXNNajg1eE1ranlYTHljTEwvdTVuMm82Qmc5My9VUmZxdU9vU0lHT0NSMjVEYVp6cHcyazNzN29FOWRNd0VXWHRmWGdZdGgyYmxqeTV0RkgwR2pwT2t4MDdqN1pUNUhueG5sc0NBd0VBQWFOQU1ENHdEQVlEVlIwVEJBVXdBd0VCL3pBTEJnTlZIUThFQkFNQ0JlQXdJUVlEVlIwUkJCb3dHSUlXZDNkM0xtaGxZV3gwYUdWa1lYUmhhVzVqTG1OdmJUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FZRUFDdU1VTnE5YXkrK2U1WUM3UUZPOTRyZnp4R1FuRjNHa2xaTkFYbUlyN1BWR2RpR1kyR1R4LzlSdEhDd1RLemtMK3l2S29qZVo5ZFZLOHdyR1ZpUmtPL2pVeVorS2NXUm9rVWpzNTluY0pHUk1TU1J4dGVDUXVqdDRoZjIrL3FWK2Yyc01RdEVyd1BFMzB2YnFSWVVOTk5CVkVRcGFReC9hY0pEVXY5djdzakhpSkRxWHdRK3J6ajk1aUhBSWFlRUhxRi9NczIycDJiZVp1cXZJUUtlTWwrc3ZWcUh0aXV6V25GNFU2VkltcGtyNGJIbDdlZ1Y5SDZsNlQyU01rajZxRFU1ZTlOZzBabExUdG9zc2hCTG1vcEY3ZTdIeXJURUFtZk9QS1FlMEVnOUUydXJ6eHFCdUc1NGs1MEcySjBGaVBzUUpBaEZpTkd3U2c0UzNIeVZERzd1ZUtkMEw5M3dLT05PWUd2TUtpekNIQitwS3ZFTUpvWjh5OXVpQitIRlhjcDlSYUpxSjk3SHBaVEV2K2xpQ3AyUFNYemNLMHIyNVNjeWpGNmRMb3VLMlNCMzB4QXZKOHRFNTg0K2pxUTZDR2VjVTlYanZsQWp1SmRDckRlVlBzamtuN09QWEcrOFhaVTd6cUhaTm1YWDFZWTRINXJnRUo0OGxyVXJQM0k4UiJdfQ..rE5gfhbdbkmuYpKPb87Tpg1nRdRJTyNfWAOmXR2AA63FGKz2br_aAFtXj2UwIVHjp_AmgOZrpKmUFZnibBRcRTBUVfPWVrgRHleP1Dd1QL2qiaz0Pxw38odIVKQw88pFXBfIASsnrFMEJ_x07XQLWaXLOcEYVBb2s1SoULIW_McJCeCVKEnhrrsmNhXvAHLxYm2y8lUdTYYMH7rCM2FEIglavxLA4QI1nPv5i5ZwSOZDI1OoPtK4dHbbvggsKnAtBJL890AJ7JD9h1QTmw8vSWjydCBNilgGiyffPmfSGXFvGQ9tEcETHW0NlUEF649Zt-k71aMhkjooCsRwM9Dmb4STqO7Fy0MzWbvdi6K46W_rf77gv75Xr_cQyvSubWumRTMompgG1qOz5LbClfKHoWWyIzBxM2u7wqBukJTAIm10DSDgFg79-5oLPIDOj19VYsnLcyYFdYIx2G3gkBhcbExDdytlKGIZ8YvIEojDfNibC3wLWwNvmptBww-hPdT7

4. base64 the JWS and add the Signature element to the Bundle

this is what would be returned in response to a direct query over-the-wire

from base64 import b64encode
from json import loads,dumps
b64_jws = b64encode(signature.encode()).decode()
sig_element = {
            "type": [  # Signature.type = Verification Signature
              {
                "system": "urn:iso-astm:E1762-95:2013",
                "code": "1.2.840.10065.1.12.1.5",
                "display": "Verification Signature"
              }
            ],
            "when": "2021-10-05T22:42:19-07:00", #system timestamp when signature created
            "who": { #Reference to the Organization who signed the Bundle
              "reference": "Organization/123"
            },
            "data": b64_jws,
             }

searchset_bundle = loads(payload)
searchset_bundle['id'] = searchset_bundle_id # add id back in
searchset_bundle['meta'] = searchset_bundle_meta # add meta back in
searchset_bundle['signature'] = sig_element
print(dumps(searchset_bundle, indent=2))
{

  "entry": [
    {
      "fullUrl": "http://hapi.fhir.org/baseR4/Condition/4ac41715-fcbd-421c-8796-9b2c9706dd3f",
      "resource": {
        "category": [
          {
            "coding": [
              {
                "code": "encounter-diagnosis",
                "display": "Encounter Diagnosis",
                "system": "http://terminology.hl7.org/CodeSystem/condition-category"
              }
            ]
          }
        ],
        "clinicalStatus": {
          "coding": [
            {
              "code": "active",
              "system": "http://terminology.hl7.org/CodeSystem/condition-clinical"
            }
          ]
        },
        "code": {
          "coding": [
            {
              "code": "1234",
              "display": "Examplitis",
              "system": "http://snomed.info/sct"
            }
          ],
          "text": "Examplitis"
        },
        "encounter": {
          "reference": "https://example.org/Encounter/5fe62cd5-bfcf-4d3b-a1e9-80d6f75d6f82"
        },
        "id": "4ac41715-fcbd-421c-8796-9b2c9706dd3f",
        "meta": {
          "lastUpdated": "2020-04-28T20:28:00.008+00:00",
          "profile": [
            "http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition"
          ],
          "source": "#cabiJIK51sD2iz4N",
          "versionId": "10"
        },
        "onsetDateTime": "2018-10-21T21:22:15-07:00",
        "recordedDate": "2018-10-21T21:22:15-07:00",
        "resourceType": "Condition",
        "subject": {
          "reference": "https://example.org/Patient/06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b"
        },
        "verificationStatus": {
          "coding": [
            {
              "code": "confirmed",
              "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status"
            }
          ]
        }
      },
      "search": {
        "mode": "match"
      }
    }
  ],
  "link": [
    {
      "relation": "self",
      "url": "http://hapi.fhir.org/baseR4/Condition?patient=06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b&clinical-status=active,recurrance,remission"
    }
  ],
  "resourceType": "Bundle",
  "total": 1,
  "type": "searchset",
  "id": "d88e2910-3c95-469d-b1f4-ab5553b471fb",
  "meta": {
    "lastUpdated": "2020-10-23T04:54:56.048+00:00"
  },
  "signature": {
    "type": [
      {
        "system": "urn:iso-astm:E1762-95:2013",
        "code": "1.2.840.10065.1.12.1.5",
        "display": "Verification Signature"
      }
    ],
    "when": "2021-10-05T22:42:19-07:00",
    "who": {
      "reference": "Organization/123"
    },
    "data": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXQwZVNJNklsSlRJaXdpZEhsd0lqb2lTbGRVSWl3aWVEVmpJanBiSWsxSlNVVXpla05EUVRCbFowRjNTVUpCWjBsS1FVOUxSbGwyVFhkU0szbFJUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOU1VkT1RWRnpkME5SV1VSV1VWRkhSWGRLVmxWNlJWUk5Ra1ZIUVRGVlJVTkJkMHRSTWtaellWZGFkbU50TlhCWlZFVlRUVUpCUjBFeFZVVkNkM2RLVlRKR01XTXlSbk5oV0ZKMlRWSlZkMFYzV1VSV1VWRkxSRUY0U1ZwWFJuTmtSMmhzVWtkR01GbFVSWGhHZWtGV1FtZE9Wa0pCVFUxRWExWjVZVmROWjFOSFJtaGplWGRuVWtaYVRrMVRWWGRKZDFsS1MyOWFTV2gyWTA1QlVXdENSbWhhYkdGSFJtaGpNRUp2V2xkR2MyUkhhR3hhUjBZd1dWUkZkV0l6U201TlFqUllSRlJKZUUxVVFYbE9la1V6VGtSSmQwNUdiMWhFVkVsNVRWUkJlVTFxUlROT1JFbDNUa1p2ZDJkWk1IaERla0ZLUW1kT1ZrSkJXVlJCYkZaVVRWSk5kMFZSV1VSV1VWRkpSRUZ3UkZsWGVIQmFiVGw1WW0xc2FFMVNTWGRGUVZsRVZsRlJTRVJCYkZSWldGWjZXVmQ0Y0dSSE9IaEdWRUZVUW1kT1ZrSkJiMDFFUldoc1dWZDRNR0ZIVmtWWldGSm9UVlJGV0UxQ1ZVZEJNVlZGUVhkM1QxSllTbkJaZVVKSldWZEdla3hEUWtWV2F6QjRTbFJCYWtKbmEzRm9hMmxIT1hjd1FrTlJSVmRHYlZadldWZEdlbEZIYUd4WlYzZ3dZVWRXYTFsWVVtaE5VelYyWTIxamQyZG5SMmxOUVRCSFExTnhSMU5KWWpORVVVVkNRVkZWUVVFMFNVSnFkMEYzWjJkSFMwRnZTVUpuVVVSd1MyTlRhMjlCVFRaelZ6SXhLM1pYVkdWSlZrOUhlREV3VFZkaGMxRjVOMVpJYVdRMmVubHhXRUZDVFN0NmJtWkNibGhsYm5sVk1Hb3hSbFIyVUcxU1prOUViMDlFV0ZaMVVGVjNSRzl0YUVOSWFDdGlZMnhYT1V0Tk1tODFOak5qZUZKTFJYWkNibUZJY25OcWR6VjVUbTE0VHpWWmFrVlNZbWgwU0dSUlpYRnJkR1IzTTFaWlJWSlNPVWh2ZUV4UE0wWnJjM3BTTWpreVNGUkNOSGhYTTNsWGJGWXpaMVJyVFZGdmVsQlRZMHBMU0ROaVJ6aFFjWEUyUVZsUVNqZERORmxDU1d4VlUyUkNUVlpzTTNGdVpVVm1aemRtZFhocFJtWlliMlprVkZadE4zSk5hV2xITjFnNWVqUXpVR1pwYkhGaFpXbHpabTEwVW5oQmJGSjNSVTVZY2tnelQzWlBSRkI1VERCeVZHNUhPRU56WWtGWVdWWkpUVzFrWkVobE5GcEdPWEJzYURrMWMybzBjRTFVYUV4MFkwcFlMMjg1V0VoTWFtZzNSVzFhZVdkS1NGZEZVWEUwVUhkR2QxcGtiV0pqWm1oRGJVOXlPRGhJT0VKaVZYSjFMemRXTm5waWMwY3hUakZEVjJ4dVpHeGlWbnB1VENzelNVMVBjamhyV0dGSVkyRnVjV1pqYTJkR1ZqUkZjbTVtYWtaS2NURlBTV0ZCYlhOTmFqZzFlRTFyYW5sWVRIbGpURXd2ZFRWdU1tODJRbWM1TXk5VlVtWnhkVTl2VTBsSFQwTlNNalZFWVZwNmNIY3lhek56TjI5Rk9XUk5kMFZYV0hSbVdHZFpkR2d5WW14cWVUVjBSa2d3UjJwd1QydDRNRGRxTjFwVU5VaHVlRzVzYzBOQmQwVkJRV0ZPUVUxRU5IZEVRVmxFVmxJd1ZFSkJWWGRCZDBWQ0wzcEJURUpuVGxaSVVUaEZRa0ZOUTBKbFFYZEpVVmxFVmxJd1VrSkNiM2RIU1VsWFpETmtNMHh0YUd4WlYzZ3dZVWRXYTFsWVVtaGhWelZxVEcxT2RtSlVRVTVDWjJ0eGFHdHBSemwzTUVKQlVYTkdRVUZQUTBGWlJVRkRkVTFWVG5FNVlYa3JLMlUxV1VNM1VVWlBPVFJ5Wm5wNFIxRnVSak5IYTJ4YVRrRlliVWx5TjFCV1IyUnBSMWt5UjFSNEx6bFNkRWhEZDFSTGVtdE1LM2wyUzI5cVpWbzVaRlpMT0hkeVIxWnBVbXRQTDJwVmVWb3JTMk5YVW05clZXcHpOVGx1WTBwSFVrMVRVMUo0ZEdWRFVYVnFkRFJvWmpJckwzRldLMll5YzAxUmRFVnlkMUJGTXpCMlluRlNXVlZPVGs1Q1ZrVlJjR0ZSZUM5aFkwcEVWWFk1ZGpkemFraHBTa1J4V0hkUkszSjZhamsxYVVoQlNXRmxSVWh4Umk5TmN6SXljREppWlZwMWNYWkpVVXRsVFd3cmMzWldjVWgwYVhWNlYyNUdORlUyVmtsdGNHdHlOR0pJYkRkbFoxWTVTRFpzTmxReVUwMXJhalp4UkZVMVpUbE9aekJhYkV4VWRHOXpjMmhDVEcxdmNFWTNaVGRJZVhKVVJVRnRaazlRUzFGbE1FVm5PVVV5ZFhKNmVIRkNkVWMxTkdzMU1FY3lTakJHYVZCelVVcEJhRVpwVGtkM1UyYzBVek5JZVZaRVJ6ZDFaVXRrTUV3NU0zZExUMDVQV1VkMlRVdHBla05JUWl0d1MzWkZUVXB2V2poNU9YVnBRaXRJUmxoamNEbFNZVXB4U2prM1NIQmFWRVYySzJ4cFEzQXlVRk5ZZW1OTE1ISXlOVk5qZVdwR05tUk1iM1ZMTWxOQ016QjRRWFpLT0hSRk5UZzBLMnB4VVRaRFIyVmpWVGxZYW5ac1FXcDFTbVJEY2tSbFZsQnphbXR1TjA5UVdFY3JPRmhhVlRkNmNVaGFUbTFZV0RGWldUUklOWEpuUlVvME9HeHlWWEpRTTBrNFVpSmRmUS4uckU1Z2ZoYmRia211WXBLUGI4N1RwZzFuUmRSSlR5TmZXQU9tWFIyQUE2M0ZHS3oyYnJfYUFGdFhqMlV3SVZIanBfQW1nT1pycEttVUZabmliQlJjUlRCVVZmUFdWcmdSSGxlUDFEZDFRTDJxaWF6MFB4dzM4b2RJVktRdzg4cEZYQmZJQVNzbnJGTUVKX3gwN1hRTFdhWExPY0VZVkJiMnMxU29VTElXX01jSkNlQ1ZLRW5ocnJzbU5oWHZBSEx4WW0yeThsVWRUWVlNSDdyQ00yRkVJZ2xhdnhMQTRRSTFuUHY1aTVad1NPWkRJMU9vUHRLNGRIYmJ2Z2dzS25BdEJKTDg5MEFKN0pEOWgxUVRtdzh2U1dqeWRDQk5pbGdHaXlmZlBtZlNHWEZ2R1E5dEVjRVRIVzBObFVFRjY0OVp0LWs3MWFNaGtqb29Dc1J3TTlEbWI0U1RxTzdGeTBNeldidmRpNks0NldfcmY3N2d2NzVYcl9jUXl2U3ViV3VtUlRNb21wZ0cxcU96NUxiQ2xmS0hvV1d5SXpCeE0ydTd3cUJ1a0pUQUltMTBEU0RnRmc3OS01b0xQSURPajE5VllzbkxjeVlGZFlJeDJHM2drQmhjYkV4RGR5dGxLR0laOFl2SUVvakRmTmliQzN3TFd3TnZtcHRCd3ctaFBkVDc="
  }
}

Receiver/Verifier Steps

1. Remove the Bundle.signature element from the Search Bundle resource

  • For this example using the python dictionary object from above. In a transaction, it would need to be stored unaltered in order to perform these next steps.
try:
  recd_signature = searchset_bundle.pop("signature")
except:
  pass
recd_signature
{'type': [{'system': 'urn:iso-astm:E1762-95:2013',
   'code': '1.2.840.10065.1.12.1.5',
   'display': 'Verification Signature'}],
 'when': '2021-10-05T22:42:19-07:00',
 'who': {'reference': 'Organization/123'},
 'data': 'ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXQwZVNJNklsSlRJaXdpZEhsd0lqb2lTbGRVSWl3aWVEVmpJanBiSWsxSlNVVXpla05EUVRCbFowRjNTVUpCWjBsS1FVOUxSbGwyVFhkU0szbFJUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOU1VkT1RWRnpkME5SV1VSV1VWRkhSWGRLVmxWNlJWUk5Ra1ZIUVRGVlJVTkJkMHRSTWtaellWZGFkbU50TlhCWlZFVlRUVUpCUjBFeFZVVkNkM2RLVlRKR01XTXlSbk5oV0ZKMlRWSlZkMFYzV1VSV1VWRkxSRUY0U1ZwWFJuTmtSMmhzVWtkR01GbFVSWGhHZWtGV1FtZE9Wa0pCVFUxRWExWjVZVmROWjFOSFJtaGplWGRuVWtaYVRrMVRWWGRKZDFsS1MyOWFTV2gyWTA1QlVXdENSbWhhYkdGSFJtaGpNRUp2V2xkR2MyUkhhR3hhUjBZd1dWUkZkV0l6U201TlFqUllSRlJKZUUxVVFYbE9la1V6VGtSSmQwNUdiMWhFVkVsNVRWUkJlVTFxUlROT1JFbDNUa1p2ZDJkWk1IaERla0ZLUW1kT1ZrSkJXVlJCYkZaVVRWSk5kMFZSV1VSV1VWRkpSRUZ3UkZsWGVIQmFiVGw1WW0xc2FFMVNTWGRGUVZsRVZsRlJTRVJCYkZSWldGWjZXVmQ0Y0dSSE9IaEdWRUZVUW1kT1ZrSkJiMDFFUldoc1dWZDRNR0ZIVmtWWldGSm9UVlJGV0UxQ1ZVZEJNVlZGUVhkM1QxSllTbkJaZVVKSldWZEdla3hEUWtWV2F6QjRTbFJCYWtKbmEzRm9hMmxIT1hjd1FrTlJSVmRHYlZadldWZEdlbEZIYUd4WlYzZ3dZVWRXYTFsWVVtaE5VelYyWTIxamQyZG5SMmxOUVRCSFExTnhSMU5KWWpORVVVVkNRVkZWUVVFMFNVSnFkMEYzWjJkSFMwRnZTVUpuVVVSd1MyTlRhMjlCVFRaelZ6SXhLM1pYVkdWSlZrOUhlREV3VFZkaGMxRjVOMVpJYVdRMmVubHhXRUZDVFN0NmJtWkNibGhsYm5sVk1Hb3hSbFIyVUcxU1prOUViMDlFV0ZaMVVGVjNSRzl0YUVOSWFDdGlZMnhYT1V0Tk1tODFOak5qZUZKTFJYWkNibUZJY25OcWR6VjVUbTE0VHpWWmFrVlNZbWgwU0dSUlpYRnJkR1IzTTFaWlJWSlNPVWh2ZUV4UE0wWnJjM3BTTWpreVNGUkNOSGhYTTNsWGJGWXpaMVJyVFZGdmVsQlRZMHBMU0ROaVJ6aFFjWEUyUVZsUVNqZERORmxDU1d4VlUyUkNUVlpzTTNGdVpVVm1aemRtZFhocFJtWlliMlprVkZadE4zSk5hV2xITjFnNWVqUXpVR1pwYkhGaFpXbHpabTEwVW5oQmJGSjNSVTVZY2tnelQzWlBSRkI1VERCeVZHNUhPRU56WWtGWVdWWkpUVzFrWkVobE5GcEdPWEJzYURrMWMybzBjRTFVYUV4MFkwcFlMMjg1V0VoTWFtZzNSVzFhZVdkS1NGZEZVWEUwVUhkR2QxcGtiV0pqWm1oRGJVOXlPRGhJT0VKaVZYSjFMemRXTm5waWMwY3hUakZEVjJ4dVpHeGlWbnB1VENzelNVMVBjamhyV0dGSVkyRnVjV1pqYTJkR1ZqUkZjbTVtYWtaS2NURlBTV0ZCYlhOTmFqZzFlRTFyYW5sWVRIbGpURXd2ZFRWdU1tODJRbWM1TXk5VlVtWnhkVTl2VTBsSFQwTlNNalZFWVZwNmNIY3lhek56TjI5Rk9XUk5kMFZYV0hSbVdHZFpkR2d5WW14cWVUVjBSa2d3UjJwd1QydDRNRGRxTjFwVU5VaHVlRzVzYzBOQmQwVkJRV0ZPUVUxRU5IZEVRVmxFVmxJd1ZFSkJWWGRCZDBWQ0wzcEJURUpuVGxaSVVUaEZRa0ZOUTBKbFFYZEpVVmxFVmxJd1VrSkNiM2RIU1VsWFpETmtNMHh0YUd4WlYzZ3dZVWRXYTFsWVVtaGhWelZxVEcxT2RtSlVRVTVDWjJ0eGFHdHBSemwzTUVKQlVYTkdRVUZQUTBGWlJVRkRkVTFWVG5FNVlYa3JLMlUxV1VNM1VVWlBPVFJ5Wm5wNFIxRnVSak5IYTJ4YVRrRlliVWx5TjFCV1IyUnBSMWt5UjFSNEx6bFNkRWhEZDFSTGVtdE1LM2wyUzI5cVpWbzVaRlpMT0hkeVIxWnBVbXRQTDJwVmVWb3JTMk5YVW05clZXcHpOVGx1WTBwSFVrMVRVMUo0ZEdWRFVYVnFkRFJvWmpJckwzRldLMll5YzAxUmRFVnlkMUJGTXpCMlluRlNXVlZPVGs1Q1ZrVlJjR0ZSZUM5aFkwcEVWWFk1ZGpkemFraHBTa1J4V0hkUkszSjZhamsxYVVoQlNXRmxSVWh4Umk5TmN6SXljREppWlZwMWNYWkpVVXRsVFd3cmMzWldjVWgwYVhWNlYyNUdORlUyVmtsdGNHdHlOR0pJYkRkbFoxWTVTRFpzTmxReVUwMXJhalp4UkZVMVpUbE9aekJhYkV4VWRHOXpjMmhDVEcxdmNFWTNaVGRJZVhKVVJVRnRaazlRUzFGbE1FVm5PVVV5ZFhKNmVIRkNkVWMxTkdzMU1FY3lTakJHYVZCelVVcEJhRVpwVGtkM1UyYzBVek5JZVZaRVJ6ZDFaVXRrTUV3NU0zZExUMDVQV1VkMlRVdHBla05JUWl0d1MzWkZUVXB2V2poNU9YVnBRaXRJUmxoamNEbFNZVXB4U2prM1NIQmFWRVYySzJ4cFEzQXlVRk5ZZW1OTE1ISXlOVk5qZVdwR05tUk1iM1ZMTWxOQ016QjRRWFpLT0hSRk5UZzBLMnB4VVRaRFIyVmpWVGxZYW5ac1FXcDFTbVJEY2tSbFZsQnphbXR1TjA5UVdFY3JPRmhhVlRkNmNVaGFUbTFZV0RGWldUUklOWEpuUlVvME9HeHlWWEpRTTBrNFVpSmRmUS4uckU1Z2ZoYmRia211WXBLUGI4N1RwZzFuUmRSSlR5TmZXQU9tWFIyQUE2M0ZHS3oyYnJfYUFGdFhqMlV3SVZIanBfQW1nT1pycEttVUZabmliQlJjUlRCVVZmUFdWcmdSSGxlUDFEZDFRTDJxaWF6MFB4dzM4b2RJVktRdzg4cEZYQmZJQVNzbnJGTUVKX3gwN1hRTFdhWExPY0VZVkJiMnMxU29VTElXX01jSkNlQ1ZLRW5ocnJzbU5oWHZBSEx4WW0yeThsVWRUWVlNSDdyQ00yRkVJZ2xhdnhMQTRRSTFuUHY1aTVad1NPWkRJMU9vUHRLNGRIYmJ2Z2dzS25BdEJKTDg5MEFKN0pEOWgxUVRtdzh2U1dqeWRDQk5pbGdHaXlmZlBtZlNHWEZ2R1E5dEVjRVRIVzBObFVFRjY0OVp0LWs3MWFNaGtqb29Dc1J3TTlEbWI0U1RxTzdGeTBNeldidmRpNks0NldfcmY3N2d2NzVYcl9jUXl2U3ViV3VtUlRNb21wZ0cxcU96NUxiQ2xmS0hvV1d5SXpCeE0ydTd3cUJ1a0pUQUltMTBEU0RnRmc3OS01b0xQSURPajE5VllzbkxjeVlGZFlJeDJHM2drQmhjYkV4RGR5dGxLR0laOFl2SUVvakRmTmliQzN3TFd3TnZtcHRCd3ctaFBkVDc='}

2. Canonicalize the bundle using IETF JSON Canonicalization Scheme (JCS):

  • Remove the id and meta elements if present before canonicalization
searchset_bundle_id = searchset_bundle.pop("id") # remove id
searchset_bundle_meta = searchset_bundle.pop("meta") # remove meta
canonical_bundle = canonicalize(searchset_bundle)
canonical_bundle
b'{"entry":[{"fullUrl":"http://hapi.fhir.org/baseR4/Condition/4ac41715-fcbd-421c-8796-9b2c9706dd3f","resource":{"category":[{"coding":[{"code":"encounter-diagnosis","display":"Encounter Diagnosis","system":"http://terminology.hl7.org/CodeSystem/condition-category"}]}],"clinicalStatus":{"coding":[{"code":"active","system":"http://terminology.hl7.org/CodeSystem/condition-clinical"}]},"code":{"coding":[{"code":"1234","display":"Examplitis","system":"http://snomed.info/sct"}],"text":"Examplitis"},"encounter":{"reference":"https://example.org/Encounter/5fe62cd5-bfcf-4d3b-a1e9-80d6f75d6f82"},"id":"4ac41715-fcbd-421c-8796-9b2c9706dd3f","meta":{"lastUpdated":"2020-04-28T20:28:00.008+00:00","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition"],"source":"#cabiJIK51sD2iz4N","versionId":"10"},"onsetDateTime":"2018-10-21T21:22:15-07:00","recordedDate":"2018-10-21T21:22:15-07:00","resourceType":"Condition","subject":{"reference":"https://example.org/Patient/06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b"},"verificationStatus":{"coding":[{"code":"confirmed","system":"http://terminology.hl7.org/CodeSystem/condition-ver-status"}]}},"search":{"mode":"match"}}],"link":[{"relation":"self","url":"http://hapi.fhir.org/baseR4/Condition?patient=06e1f0dd-5fbe-4480-9bb4-6b54ec02d31b&clinical-status=active,recurrance,remission"}],"resourceType":"Bundle","total":1,"type":"searchset"}'

3. Transform canonicalize Bundle to a base64 format using the Base64-URL algorithm.

from base64 import urlsafe_b64encode
recd_b64_canonical_bundle  = urlsafe_b64encode(canonical_bundle).decode()
recd_b64_canonical_bundle = recd_b64_canonical_bundle.replace("=","") #remove end padding to normalize
recd_b64_canonical_bundle
'eyJlbnRyeSI6W3siZnVsbFVybCI6Imh0dHA6Ly9oYXBpLmZoaXIub3JnL2Jhc2VSNC9Db25kaXRpb24vNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwicmVzb3VyY2UiOnsiY2F0ZWdvcnkiOlt7ImNvZGluZyI6W3siY29kZSI6ImVuY291bnRlci1kaWFnbm9zaXMiLCJkaXNwbGF5IjoiRW5jb3VudGVyIERpYWdub3NpcyIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNhdGVnb3J5In1dfV0sImNsaW5pY2FsU3RhdHVzIjp7ImNvZGluZyI6W3siY29kZSI6ImFjdGl2ZSIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsIn1dfSwiY29kZSI6eyJjb2RpbmciOlt7ImNvZGUiOiIxMjM0IiwiZGlzcGxheSI6IkV4YW1wbGl0aXMiLCJzeXN0ZW0iOiJodHRwOi8vc25vbWVkLmluZm8vc2N0In1dLCJ0ZXh0IjoiRXhhbXBsaXRpcyJ9LCJlbmNvdW50ZXIiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9FbmNvdW50ZXIvNWZlNjJjZDUtYmZjZi00ZDNiLWExZTktODBkNmY3NWQ2ZjgyIn0sImlkIjoiNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwibWV0YSI6eyJsYXN0VXBkYXRlZCI6IjIwMjAtMDQtMjhUMjA6Mjg6MDAuMDA4KzAwOjAwIiwicHJvZmlsZSI6WyJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWNvbmRpdGlvbiJdLCJzb3VyY2UiOiIjY2FiaUpJSzUxc0QyaXo0TiIsInZlcnNpb25JZCI6IjEwIn0sIm9uc2V0RGF0ZVRpbWUiOiIyMDE4LTEwLTIxVDIxOjIyOjE1LTA3OjAwIiwicmVjb3JkZWREYXRlIjoiMjAxOC0xMC0yMVQyMToyMjoxNS0wNzowMCIsInJlc291cmNlVHlwZSI6IkNvbmRpdGlvbiIsInN1YmplY3QiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9QYXRpZW50LzA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiJ9LCJ2ZXJpZmljYXRpb25TdGF0dXMiOnsiY29kaW5nIjpbeyJjb2RlIjoiY29uZmlybWVkIiwic3lzdGVtIjoiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tdmVyLXN0YXR1cyJ9XX19LCJzZWFyY2giOnsibW9kZSI6Im1hdGNoIn19XSwibGluayI6W3sicmVsYXRpb24iOiJzZWxmIiwidXJsIjoiaHR0cDovL2hhcGkuZmhpci5vcmcvYmFzZVI0L0NvbmRpdGlvbj9wYXRpZW50PTA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiZjbGluaWNhbC1zdGF0dXM9YWN0aXZlLHJlY3VycmFuY2UscmVtaXNzaW9uIn1dLCJyZXNvdXJjZVR5cGUiOiJCdW5kbGUiLCJ0b3RhbCI6MSwidHlwZSI6InNlYXJjaHNldCJ9'

4. Get the base64 encoded JWS from the Bundle.signature.data element

recd_b64_jws = recd_signature['data']
recd_b64_jws
'ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXQwZVNJNklsSlRJaXdpZEhsd0lqb2lTbGRVSWl3aWVEVmpJanBiSWsxSlNVVXpla05EUVRCbFowRjNTVUpCWjBsS1FVOUxSbGwyVFhkU0szbFJUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOU1VkT1RWRnpkME5SV1VSV1VWRkhSWGRLVmxWNlJWUk5Ra1ZIUVRGVlJVTkJkMHRSTWtaellWZGFkbU50TlhCWlZFVlRUVUpCUjBFeFZVVkNkM2RLVlRKR01XTXlSbk5oV0ZKMlRWSlZkMFYzV1VSV1VWRkxSRUY0U1ZwWFJuTmtSMmhzVWtkR01GbFVSWGhHZWtGV1FtZE9Wa0pCVFUxRWExWjVZVmROWjFOSFJtaGplWGRuVWtaYVRrMVRWWGRKZDFsS1MyOWFTV2gyWTA1QlVXdENSbWhhYkdGSFJtaGpNRUp2V2xkR2MyUkhhR3hhUjBZd1dWUkZkV0l6U201TlFqUllSRlJKZUUxVVFYbE9la1V6VGtSSmQwNUdiMWhFVkVsNVRWUkJlVTFxUlROT1JFbDNUa1p2ZDJkWk1IaERla0ZLUW1kT1ZrSkJXVlJCYkZaVVRWSk5kMFZSV1VSV1VWRkpSRUZ3UkZsWGVIQmFiVGw1WW0xc2FFMVNTWGRGUVZsRVZsRlJTRVJCYkZSWldGWjZXVmQ0Y0dSSE9IaEdWRUZVUW1kT1ZrSkJiMDFFUldoc1dWZDRNR0ZIVmtWWldGSm9UVlJGV0UxQ1ZVZEJNVlZGUVhkM1QxSllTbkJaZVVKSldWZEdla3hEUWtWV2F6QjRTbFJCYWtKbmEzRm9hMmxIT1hjd1FrTlJSVmRHYlZadldWZEdlbEZIYUd4WlYzZ3dZVWRXYTFsWVVtaE5VelYyWTIxamQyZG5SMmxOUVRCSFExTnhSMU5KWWpORVVVVkNRVkZWUVVFMFNVSnFkMEYzWjJkSFMwRnZTVUpuVVVSd1MyTlRhMjlCVFRaelZ6SXhLM1pYVkdWSlZrOUhlREV3VFZkaGMxRjVOMVpJYVdRMmVubHhXRUZDVFN0NmJtWkNibGhsYm5sVk1Hb3hSbFIyVUcxU1prOUViMDlFV0ZaMVVGVjNSRzl0YUVOSWFDdGlZMnhYT1V0Tk1tODFOak5qZUZKTFJYWkNibUZJY25OcWR6VjVUbTE0VHpWWmFrVlNZbWgwU0dSUlpYRnJkR1IzTTFaWlJWSlNPVWh2ZUV4UE0wWnJjM3BTTWpreVNGUkNOSGhYTTNsWGJGWXpaMVJyVFZGdmVsQlRZMHBMU0ROaVJ6aFFjWEUyUVZsUVNqZERORmxDU1d4VlUyUkNUVlpzTTNGdVpVVm1aemRtZFhocFJtWlliMlprVkZadE4zSk5hV2xITjFnNWVqUXpVR1pwYkhGaFpXbHpabTEwVW5oQmJGSjNSVTVZY2tnelQzWlBSRkI1VERCeVZHNUhPRU56WWtGWVdWWkpUVzFrWkVobE5GcEdPWEJzYURrMWMybzBjRTFVYUV4MFkwcFlMMjg1V0VoTWFtZzNSVzFhZVdkS1NGZEZVWEUwVUhkR2QxcGtiV0pqWm1oRGJVOXlPRGhJT0VKaVZYSjFMemRXTm5waWMwY3hUakZEVjJ4dVpHeGlWbnB1VENzelNVMVBjamhyV0dGSVkyRnVjV1pqYTJkR1ZqUkZjbTVtYWtaS2NURlBTV0ZCYlhOTmFqZzFlRTFyYW5sWVRIbGpURXd2ZFRWdU1tODJRbWM1TXk5VlVtWnhkVTl2VTBsSFQwTlNNalZFWVZwNmNIY3lhek56TjI5Rk9XUk5kMFZYV0hSbVdHZFpkR2d5WW14cWVUVjBSa2d3UjJwd1QydDRNRGRxTjFwVU5VaHVlRzVzYzBOQmQwVkJRV0ZPUVUxRU5IZEVRVmxFVmxJd1ZFSkJWWGRCZDBWQ0wzcEJURUpuVGxaSVVUaEZRa0ZOUTBKbFFYZEpVVmxFVmxJd1VrSkNiM2RIU1VsWFpETmtNMHh0YUd4WlYzZ3dZVWRXYTFsWVVtaGhWelZxVEcxT2RtSlVRVTVDWjJ0eGFHdHBSemwzTUVKQlVYTkdRVUZQUTBGWlJVRkRkVTFWVG5FNVlYa3JLMlUxV1VNM1VVWlBPVFJ5Wm5wNFIxRnVSak5IYTJ4YVRrRlliVWx5TjFCV1IyUnBSMWt5UjFSNEx6bFNkRWhEZDFSTGVtdE1LM2wyUzI5cVpWbzVaRlpMT0hkeVIxWnBVbXRQTDJwVmVWb3JTMk5YVW05clZXcHpOVGx1WTBwSFVrMVRVMUo0ZEdWRFVYVnFkRFJvWmpJckwzRldLMll5YzAxUmRFVnlkMUJGTXpCMlluRlNXVlZPVGs1Q1ZrVlJjR0ZSZUM5aFkwcEVWWFk1ZGpkemFraHBTa1J4V0hkUkszSjZhamsxYVVoQlNXRmxSVWh4Umk5TmN6SXljREppWlZwMWNYWkpVVXRsVFd3cmMzWldjVWgwYVhWNlYyNUdORlUyVmtsdGNHdHlOR0pJYkRkbFoxWTVTRFpzTmxReVUwMXJhalp4UkZVMVpUbE9aekJhYkV4VWRHOXpjMmhDVEcxdmNFWTNaVGRJZVhKVVJVRnRaazlRUzFGbE1FVm5PVVV5ZFhKNmVIRkNkVWMxTkdzMU1FY3lTakJHYVZCelVVcEJhRVpwVGtkM1UyYzBVek5JZVZaRVJ6ZDFaVXRrTUV3NU0zZExUMDVQV1VkMlRVdHBla05JUWl0d1MzWkZUVXB2V2poNU9YVnBRaXRJUmxoamNEbFNZVXB4U2prM1NIQmFWRVYySzJ4cFEzQXlVRk5ZZW1OTE1ISXlOVk5qZVdwR05tUk1iM1ZMTWxOQ016QjRRWFpLT0hSRk5UZzBLMnB4VVRaRFIyVmpWVGxZYW5ac1FXcDFTbVJEY2tSbFZsQnphbXR1TjA5UVdFY3JPRmhhVlRkNmNVaGFUbTFZV0RGWldUUklOWEpuUlVvME9HeHlWWEpRTTBrNFVpSmRmUS4uckU1Z2ZoYmRia211WXBLUGI4N1RwZzFuUmRSSlR5TmZXQU9tWFIyQUE2M0ZHS3oyYnJfYUFGdFhqMlV3SVZIanBfQW1nT1pycEttVUZabmliQlJjUlRCVVZmUFdWcmdSSGxlUDFEZDFRTDJxaWF6MFB4dzM4b2RJVktRdzg4cEZYQmZJQVNzbnJGTUVKX3gwN1hRTFdhWExPY0VZVkJiMnMxU29VTElXX01jSkNlQ1ZLRW5ocnJzbU5oWHZBSEx4WW0yeThsVWRUWVlNSDdyQ00yRkVJZ2xhdnhMQTRRSTFuUHY1aTVad1NPWkRJMU9vUHRLNGRIYmJ2Z2dzS25BdEJKTDg5MEFKN0pEOWgxUVRtdzh2U1dqeWRDQk5pbGdHaXlmZlBtZlNHWEZ2R1E5dEVjRVRIVzBObFVFRjY0OVp0LWs3MWFNaGtqb29Dc1J3TTlEbWI0U1RxTzdGeTBNeldidmRpNks0NldfcmY3N2d2NzVYcl9jUXl2U3ViV3VtUlRNb21wZ0cxcU96NUxiQ2xmS0hvV1d5SXpCeE0ydTd3cUJ1a0pUQUltMTBEU0RnRmc3OS01b0xQSURPajE5VllzbkxjeVlGZFlJeDJHM2drQmhjYkV4RGR5dGxLR0laOFl2SUVvakRmTmliQzN3TFd3TnZtcHRCd3ctaFBkVDc='

5. Base64 decode the encoded JWS

note the signature is displayed with the parts labeled and separated with line breaks for easier viewing

from base64 import b64decode
recd_jws = b64decode(recd_b64_jws.encode()).decode()
for i,j in enumerate(recd_jws.split('.')):
    print(f'{labels[i]}:')
    print(f'{j}')
    print()
header:
eyJhbGciOiJSUzI1NiIsImt0eSI6IlJTIiwidHlwIjoiSldUIiwieDVjIjpbIk1JSUUzekNDQTBlZ0F3SUJBZ0lKQU9LRll2TXdSK3lRTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdOTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKVTJGMWMyRnNhWFJ2TVJVd0V3WURWUVFLREF4SVpXRnNkR2hsUkdGMFlURXhGekFWQmdOVkJBTU1Ea1Z5YVdNZ1NHRmhjeXdnUkZaTk1TVXdJd1lKS29aSWh2Y05BUWtCRmhabGFHRmhjMEJvWldGc2RHaGxaR0YwWVRFdWIzSm5NQjRYRFRJeE1UQXlOekUzTkRJd05Gb1hEVEl5TVRBeU1qRTNOREl3TkZvd2dZMHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbFRZWFZ6WVd4cGRHOHhGVEFUQmdOVkJBb01ERWhsWVd4MGFHVkVZWFJoTVRFWE1CVUdBMVVFQXd3T1JYSnBZeUJJWVdGekxDQkVWazB4SlRBakJna3Foa2lHOXcwQkNRRVdGbVZvWVdGelFHaGxZV3gwYUdWa1lYUmhNUzV2Y21jd2dnR2lNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUURwS2NTa29BTTZzVzIxK3ZXVGVJVk9HeDEwTVdhc1F5N1ZIaWQ2enlxWEFCTSt6bmZCblhlbnlVMGoxRlR2UG1SZk9Eb09EWFZ1UFV3RG9taENIaCtiY2xXOUtNMm81NjNjeFJLRXZCbmFIcnNqdzV5Tm14TzVZakVSYmh0SGRRZXFrdGR3M1ZZRVJSOUhveExPM0Zrc3pSMjkySFRCNHhXM3lXbFYzZ1RrTVFvelBTY0pLSDNiRzhQcXE2QVlQSjdDNFlCSWxVU2RCTVZsM3FuZUVmZzdmdXhpRmZYb2ZkVFZtN3JNaWlHN1g5ejQzUGZpbHFhZWlzZm10UnhBbFJ3RU5YckgzT3ZPRFB5TDByVG5HOENzYkFYWVZJTW1kZEhlNFpGOXBsaDk1c2o0cE1UaEx0Y0pYL285WEhMamg3RW1aeWdKSFdFUXE0UHdGd1pkbWJjZmhDbU9yODhIOEJiVXJ1LzdWNnpic0cxTjFDV2xuZGxiVnpuTCszSU1PcjhrWGFIY2FucWZja2dGVjRFcm5makZKcTFPSWFBbXNNajg1eE1ranlYTHljTEwvdTVuMm82Qmc5My9VUmZxdU9vU0lHT0NSMjVEYVp6cHcyazNzN29FOWRNd0VXWHRmWGdZdGgyYmxqeTV0RkgwR2pwT2t4MDdqN1pUNUhueG5sc0NBd0VBQWFOQU1ENHdEQVlEVlIwVEJBVXdBd0VCL3pBTEJnTlZIUThFQkFNQ0JlQXdJUVlEVlIwUkJCb3dHSUlXZDNkM0xtaGxZV3gwYUdWa1lYUmhhVzVqTG1OdmJUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FZRUFDdU1VTnE5YXkrK2U1WUM3UUZPOTRyZnp4R1FuRjNHa2xaTkFYbUlyN1BWR2RpR1kyR1R4LzlSdEhDd1RLemtMK3l2S29qZVo5ZFZLOHdyR1ZpUmtPL2pVeVorS2NXUm9rVWpzNTluY0pHUk1TU1J4dGVDUXVqdDRoZjIrL3FWK2Yyc01RdEVyd1BFMzB2YnFSWVVOTk5CVkVRcGFReC9hY0pEVXY5djdzakhpSkRxWHdRK3J6ajk1aUhBSWFlRUhxRi9NczIycDJiZVp1cXZJUUtlTWwrc3ZWcUh0aXV6V25GNFU2VkltcGtyNGJIbDdlZ1Y5SDZsNlQyU01rajZxRFU1ZTlOZzBabExUdG9zc2hCTG1vcEY3ZTdIeXJURUFtZk9QS1FlMEVnOUUydXJ6eHFCdUc1NGs1MEcySjBGaVBzUUpBaEZpTkd3U2c0UzNIeVZERzd1ZUtkMEw5M3dLT05PWUd2TUtpekNIQitwS3ZFTUpvWjh5OXVpQitIRlhjcDlSYUpxSjk3SHBaVEV2K2xpQ3AyUFNYemNLMHIyNVNjeWpGNmRMb3VLMlNCMzB4QXZKOHRFNTg0K2pxUTZDR2VjVTlYanZsQWp1SmRDckRlVlBzamtuN09QWEcrOFhaVTd6cUhaTm1YWDFZWTRINXJnRUo0OGxyVXJQM0k4UiJdfQ

payload:


signature:
rE5gfhbdbkmuYpKPb87Tpg1nRdRJTyNfWAOmXR2AA63FGKz2br_aAFtXj2UwIVHjp_AmgOZrpKmUFZnibBRcRTBUVfPWVrgRHleP1Dd1QL2qiaz0Pxw38odIVKQw88pFXBfIASsnrFMEJ_x07XQLWaXLOcEYVBb2s1SoULIW_McJCeCVKEnhrrsmNhXvAHLxYm2y8lUdTYYMH7rCM2FEIglavxLA4QI1nPv5i5ZwSOZDI1OoPtK4dHbbvggsKnAtBJL890AJ7JD9h1QTmw8vSWjydCBNilgGiyffPmfSGXFvGQ9tEcETHW0NlUEF649Zt-k71aMhkjooCsRwM9Dmb4STqO7Fy0MzWbvdi6K46W_rf77gv75Xr_cQyvSubWumRTMompgG1qOz5LbClfKHoWWyIzBxM2u7wqBukJTAIm10DSDgFg79-5oLPIDOj19VYsnLcyYFdYIx2G3gkBhcbExDdytlKGIZ8YvIEojDfNibC3wLWwNvmptBww-hPdT7

**6. reattach the payload - the base64 encoded Bundle - into the JWS payload element. **

note the signature is displayed with the parts labeled and separated with line breaks for easier viewing

split_sig = recd_jws.split('.')
split_sig[1] = recd_b64_canonical_bundle
recd_jws = '.'.join(split_sig)
for i,j in enumerate(recd_jws.split('.')):
    print(f'{labels[i]}:')
    print(f'{j}')
    print()
header:
eyJhbGciOiJSUzI1NiIsImt0eSI6IlJTIiwidHlwIjoiSldUIiwieDVjIjpbIk1JSUUzekNDQTBlZ0F3SUJBZ0lKQU9LRll2TXdSK3lRTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdOTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKVTJGMWMyRnNhWFJ2TVJVd0V3WURWUVFLREF4SVpXRnNkR2hsUkdGMFlURXhGekFWQmdOVkJBTU1Ea1Z5YVdNZ1NHRmhjeXdnUkZaTk1TVXdJd1lKS29aSWh2Y05BUWtCRmhabGFHRmhjMEJvWldGc2RHaGxaR0YwWVRFdWIzSm5NQjRYRFRJeE1UQXlOekUzTkRJd05Gb1hEVEl5TVRBeU1qRTNOREl3TkZvd2dZMHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbFRZWFZ6WVd4cGRHOHhGVEFUQmdOVkJBb01ERWhsWVd4MGFHVkVZWFJoTVRFWE1CVUdBMVVFQXd3T1JYSnBZeUJJWVdGekxDQkVWazB4SlRBakJna3Foa2lHOXcwQkNRRVdGbVZvWVdGelFHaGxZV3gwYUdWa1lYUmhNUzV2Y21jd2dnR2lNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUURwS2NTa29BTTZzVzIxK3ZXVGVJVk9HeDEwTVdhc1F5N1ZIaWQ2enlxWEFCTSt6bmZCblhlbnlVMGoxRlR2UG1SZk9Eb09EWFZ1UFV3RG9taENIaCtiY2xXOUtNMm81NjNjeFJLRXZCbmFIcnNqdzV5Tm14TzVZakVSYmh0SGRRZXFrdGR3M1ZZRVJSOUhveExPM0Zrc3pSMjkySFRCNHhXM3lXbFYzZ1RrTVFvelBTY0pLSDNiRzhQcXE2QVlQSjdDNFlCSWxVU2RCTVZsM3FuZUVmZzdmdXhpRmZYb2ZkVFZtN3JNaWlHN1g5ejQzUGZpbHFhZWlzZm10UnhBbFJ3RU5YckgzT3ZPRFB5TDByVG5HOENzYkFYWVZJTW1kZEhlNFpGOXBsaDk1c2o0cE1UaEx0Y0pYL285WEhMamg3RW1aeWdKSFdFUXE0UHdGd1pkbWJjZmhDbU9yODhIOEJiVXJ1LzdWNnpic0cxTjFDV2xuZGxiVnpuTCszSU1PcjhrWGFIY2FucWZja2dGVjRFcm5makZKcTFPSWFBbXNNajg1eE1ranlYTHljTEwvdTVuMm82Qmc5My9VUmZxdU9vU0lHT0NSMjVEYVp6cHcyazNzN29FOWRNd0VXWHRmWGdZdGgyYmxqeTV0RkgwR2pwT2t4MDdqN1pUNUhueG5sc0NBd0VBQWFOQU1ENHdEQVlEVlIwVEJBVXdBd0VCL3pBTEJnTlZIUThFQkFNQ0JlQXdJUVlEVlIwUkJCb3dHSUlXZDNkM0xtaGxZV3gwYUdWa1lYUmhhVzVqTG1OdmJUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FZRUFDdU1VTnE5YXkrK2U1WUM3UUZPOTRyZnp4R1FuRjNHa2xaTkFYbUlyN1BWR2RpR1kyR1R4LzlSdEhDd1RLemtMK3l2S29qZVo5ZFZLOHdyR1ZpUmtPL2pVeVorS2NXUm9rVWpzNTluY0pHUk1TU1J4dGVDUXVqdDRoZjIrL3FWK2Yyc01RdEVyd1BFMzB2YnFSWVVOTk5CVkVRcGFReC9hY0pEVXY5djdzakhpSkRxWHdRK3J6ajk1aUhBSWFlRUhxRi9NczIycDJiZVp1cXZJUUtlTWwrc3ZWcUh0aXV6V25GNFU2VkltcGtyNGJIbDdlZ1Y5SDZsNlQyU01rajZxRFU1ZTlOZzBabExUdG9zc2hCTG1vcEY3ZTdIeXJURUFtZk9QS1FlMEVnOUUydXJ6eHFCdUc1NGs1MEcySjBGaVBzUUpBaEZpTkd3U2c0UzNIeVZERzd1ZUtkMEw5M3dLT05PWUd2TUtpekNIQitwS3ZFTUpvWjh5OXVpQitIRlhjcDlSYUpxSjk3SHBaVEV2K2xpQ3AyUFNYemNLMHIyNVNjeWpGNmRMb3VLMlNCMzB4QXZKOHRFNTg0K2pxUTZDR2VjVTlYanZsQWp1SmRDckRlVlBzamtuN09QWEcrOFhaVTd6cUhaTm1YWDFZWTRINXJnRUo0OGxyVXJQM0k4UiJdfQ

payload:
eyJlbnRyeSI6W3siZnVsbFVybCI6Imh0dHA6Ly9oYXBpLmZoaXIub3JnL2Jhc2VSNC9Db25kaXRpb24vNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwicmVzb3VyY2UiOnsiY2F0ZWdvcnkiOlt7ImNvZGluZyI6W3siY29kZSI6ImVuY291bnRlci1kaWFnbm9zaXMiLCJkaXNwbGF5IjoiRW5jb3VudGVyIERpYWdub3NpcyIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNhdGVnb3J5In1dfV0sImNsaW5pY2FsU3RhdHVzIjp7ImNvZGluZyI6W3siY29kZSI6ImFjdGl2ZSIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsIn1dfSwiY29kZSI6eyJjb2RpbmciOlt7ImNvZGUiOiIxMjM0IiwiZGlzcGxheSI6IkV4YW1wbGl0aXMiLCJzeXN0ZW0iOiJodHRwOi8vc25vbWVkLmluZm8vc2N0In1dLCJ0ZXh0IjoiRXhhbXBsaXRpcyJ9LCJlbmNvdW50ZXIiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9FbmNvdW50ZXIvNWZlNjJjZDUtYmZjZi00ZDNiLWExZTktODBkNmY3NWQ2ZjgyIn0sImlkIjoiNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwibWV0YSI6eyJsYXN0VXBkYXRlZCI6IjIwMjAtMDQtMjhUMjA6Mjg6MDAuMDA4KzAwOjAwIiwicHJvZmlsZSI6WyJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWNvbmRpdGlvbiJdLCJzb3VyY2UiOiIjY2FiaUpJSzUxc0QyaXo0TiIsInZlcnNpb25JZCI6IjEwIn0sIm9uc2V0RGF0ZVRpbWUiOiIyMDE4LTEwLTIxVDIxOjIyOjE1LTA3OjAwIiwicmVjb3JkZWREYXRlIjoiMjAxOC0xMC0yMVQyMToyMjoxNS0wNzowMCIsInJlc291cmNlVHlwZSI6IkNvbmRpdGlvbiIsInN1YmplY3QiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9QYXRpZW50LzA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiJ9LCJ2ZXJpZmljYXRpb25TdGF0dXMiOnsiY29kaW5nIjpbeyJjb2RlIjoiY29uZmlybWVkIiwic3lzdGVtIjoiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tdmVyLXN0YXR1cyJ9XX19LCJzZWFyY2giOnsibW9kZSI6Im1hdGNoIn19XSwibGluayI6W3sicmVsYXRpb24iOiJzZWxmIiwidXJsIjoiaHR0cDovL2hhcGkuZmhpci5vcmcvYmFzZVI0L0NvbmRpdGlvbj9wYXRpZW50PTA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiZjbGluaWNhbC1zdGF0dXM9YWN0aXZlLHJlY3VycmFuY2UscmVtaXNzaW9uIn1dLCJyZXNvdXJjZVR5cGUiOiJCdW5kbGUiLCJ0b3RhbCI6MSwidHlwZSI6InNlYXJjaHNldCJ9

signature:
rE5gfhbdbkmuYpKPb87Tpg1nRdRJTyNfWAOmXR2AA63FGKz2br_aAFtXj2UwIVHjp_AmgOZrpKmUFZnibBRcRTBUVfPWVrgRHleP1Dd1QL2qiaz0Pxw38odIVKQw88pFXBfIASsnrFMEJ_x07XQLWaXLOcEYVBb2s1SoULIW_McJCeCVKEnhrrsmNhXvAHLxYm2y8lUdTYYMH7rCM2FEIglavxLA4QI1nPv5i5ZwSOZDI1OoPtK4dHbbvggsKnAtBJL890AJ7JD9h1QTmw8vSWjydCBNilgGiyffPmfSGXFvGQ9tEcETHW0NlUEF649Zt-k71aMhkjooCsRwM9Dmb4STqO7Fy0MzWbvdi6K46W_rf77gv75Xr_cQyvSubWumRTMompgG1qOz5LbClfKHoWWyIzBxM2u7wqBukJTAIm10DSDgFg79-5oLPIDOj19VYsnLcyYFdYIx2G3gkBhcbExDdytlKGIZ8YvIEojDfNibC3wLWwNvmptBww-hPdT7

7. Obtain public Key from the first certificate in JWS header "x5c" key

  • base64 decode the key value
  • Verify Issuer, Validity Dates, Subject,and KeyUsage of certificate,
  • Get the “Subject Public Key Info” from the cert to verify the signature
recd_header = jws.get_unverified_header(recd_jws)
recd_header
{'alg': 'RS256',
   'kty': 'RS',
   'typ': 'JWT',
   'x5c': ['MIIE3zCCA0egAwIBAgIJAOKFYvMwR+yQMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU2F1c2FsaXRvMRUwEwYDVQQKDAxIZWFsdGhlRGF0YTExFzAVBgNVBAMMDkVyaWMgSGFhcywgRFZNMSUwIwYJKoZIhvcNAQkBFhZlaGFhc0BoZWFsdGhlZGF0YTEub3JnMB4XDTIxMTAyNzE3NDIwNFoXDTIyMTAyMjE3NDIwNFowgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYXVzYWxpdG8xFTATBgNVBAoMDEhlYWx0aGVEYXRhMTEXMBUGA1UEAwwORXJpYyBIYWFzLCBEVk0xJTAjBgkqhkiG9w0BCQEWFmVoYWFzQGhlYWx0aGVkYXRhMS5vcmcwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDpKcSkoAM6sW21+vWTeIVOGx10MWasQy7VHid6zyqXABM+znfBnXenyU0j1FTvPmRfODoODXVuPUwDomhCHh+bclW9KM2o563cxRKEvBnaHrsjw5yNmxO5YjERbhtHdQeqktdw3VYERR9HoxLO3FkszR292HTB4xW3yWlV3gTkMQozPScJKH3bG8Pqq6AYPJ7C4YBIlUSdBMVl3qneEfg7fuxiFfXofdTVm7rMiiG7X9z43PfilqaeisfmtRxAlRwENXrH3OvODPyL0rTnG8CsbAXYVIMmddHe4ZF9plh95sj4pMThLtcJX/o9XHLjh7EmZygJHWEQq4PwFwZdmbcfhCmOr88H8BbUru/7V6zbsG1N1CWlndlbVznL+3IMOr8kXaHcanqfckgFV4ErnfjFJq1OIaAmsMj85xMkjyXLycLL/u5n2o6Bg93/URfquOoSIGOCR25DaZzpw2k3s7oE9dMwEWXtfXgYth2bljy5tFH0GjpOkx07j7ZT5HnxnlsCAwEAAaNAMD4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCBeAwIQYDVR0RBBowGIIWd3d3LmhlYWx0aGVkYXRhaW5jLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEACuMUNq9ay++e5YC7QFO94rfzxGQnF3GklZNAXmIr7PVGdiGY2GTx/9RtHCwTKzkL+yvKojeZ9dVK8wrGViRkO/jUyZ+KcWRokUjs59ncJGRMSSRxteCQujt4hf2+/qV+f2sMQtErwPE30vbqRYUNNNBVEQpaQx/acJDUv9v7sjHiJDqXwQ+rzj95iHAIaeEHqF/Ms22p2beZuqvIQKeMl+svVqHtiuzWnF4U6VImpkr4bHl7egV9H6l6T2SMkj6qDU5e9Ng0ZlLTtosshBLmopF7e7HyrTEAmfOPKQe0Eg9E2urzxqBuG54k50G2J0FiPsQJAhFiNGwSg4S3HyVDG7ueKd0L93wKONOYGvMKizCHB+pKvEMJoZ8y9uiB+HFXcp9RaJqJ97HpZTEv+liCp2PSXzcK0r25ScyjF6dLouK2SB30xAvJ8tE584+jqQ6CGecU9XjvlAjuJdCrDeVPsjkn7OPXG+8XZU7zqHZNmXX1YY4H5rgEJ48lrUrP3I8R']}
recd_cert = b64decode(recd_header['x5c'][0])
with open('recd_cert.der', 'wb') as f:
    f.write(recd_cert)
!openssl x509 -in recd_cert.der -inform DER -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 16322561221100825744 (0xe28562f33047ec90)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=California, L=Sausalito, O=HealtheData1, CN=Eric Haas, DVM/emailAddress=ehaas@healthedata1.org
        Validity
            Not Before: Oct 27 17:42:04 2021 GMT
            Not After : Oct 22 17:42:04 2022 GMT
        Subject: C=US, ST=California, L=Sausalito, O=HealtheData1, CN=Eric Haas, DVM/emailAddress=ehaas@healthedata1.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (3072 bit)
                Modulus:
                    00:e9:29:c4:a4:a0:03:3a:b1:6d:b5:fa:f5:93:78:
                    85:4e:1b:1d:74:31:66:ac:43:2e:d5:1e:27:7a:cf:
                    2a:97:00:13:3e:ce:77:c1:9d:77:a7:c9:4d:23:d4:
                    54:ef:3e:64:5f:38:3a:0e:0d:75:6e:3d:4c:03:a2:
                    68:42:1e:1f:9b:72:55:bd:28:cd:a8:e7:ad:dc:c5:
                    12:84:bc:19:da:1e:bb:23:c3:9c:8d:9b:13:b9:62:
                    31:11:6e:1b:47:75:07:aa:92:d7:70:dd:56:04:45:
                    1f:47:a3:12:ce:dc:59:2c:cd:1d:bd:d8:74:c1:e3:
                    15:b7:c9:69:55:de:04:e4:31:0a:33:3d:27:09:28:
                    7d:db:1b:c3:ea:ab:a0:18:3c:9e:c2:e1:80:48:95:
                    44:9d:04:c5:65:de:a9:de:11:f8:3b:7e:ec:62:15:
                    f5:e8:7d:d4:d5:9b:ba:cc:8a:21:bb:5f:dc:f8:dc:
                    f7:e2:96:a6:9e:8a:c7:e6:b5:1c:40:95:1c:04:35:
                    7a:c7:dc:eb:ce:0c:fc:8b:d2:b4:e7:1b:c0:ac:6c:
                    05:d8:54:83:26:75:d1:de:e1:91:7d:a6:58:7d:e6:
                    c8:f8:a4:c4:e1:2e:d7:09:5f:fa:3d:5c:72:e3:87:
                    b1:26:67:28:09:1d:61:10:ab:83:f0:17:06:5d:99:
                    b7:1f:84:29:8e:af:cf:07:f0:16:d4:ae:ef:fb:57:
                    ac:db:b0:6d:4d:d4:25:a5:9d:d9:5b:57:39:cb:fb:
                    72:0c:3a:bf:24:5d:a1:dc:6a:7a:9f:72:48:05:57:
                    81:2b:9d:f8:c5:26:ad:4e:21:a0:26:b0:c8:fc:e7:
                    13:24:8f:25:cb:c9:c2:cb:fe:ee:67:da:8e:81:83:
                    dd:ff:51:17:ea:b8:ea:12:20:63:82:47:6e:43:69:
                    9c:e9:c3:69:37:b3:ba:04:f5:d3:30:11:65:ed:7d:
                    78:18:b6:1d:9b:96:3c:b9:b4:51:f4:1a:3a:4e:93:
                    1d:3b:8f:b6:53:e4:79:f1:9e:5b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:TRUE
            X509v3 Key Usage:
                Digital Signature, Non Repudiation, Key Encipherment
            X509v3 Subject Alternative Name:
                DNS:www.healthedatainc.com
    Signature Algorithm: sha256WithRSAEncryption
         0a:e3:14:36:af:5a:cb:ef:9e:e5:80:bb:40:53:bd:e2:b7:f3:
         c4:64:27:17:71:a4:95:93:40:5e:62:2b:ec:f5:46:76:21:98:
         d8:64:f1:ff:d4:6d:1c:2c:13:2b:39:0b:fb:2b:ca:a2:37:99:
         f5:d5:4a:f3:0a:c6:56:24:64:3b:f8:d4:c9:9f:8a:71:64:68:
         91:48:ec:e7:d9:dc:24:64:4c:49:24:71:b5:e0:90:ba:3b:78:
         85:fd:be:fe:a5:7e:7f:6b:0c:42:d1:2b:c0:f1:37:d2:f6:ea:
         45:85:0d:34:d0:55:11:0a:5a:43:1f:da:70:90:d4:bf:db:fb:
         b2:31:e2:24:3a:97:c1:0f:ab:ce:3f:79:88:70:08:69:e1:07:
         a8:5f:cc:b3:6d:a9:d9:b7:99:ba:ab:c8:40:a7:8c:97:eb:2f:
         56:a1:ed:8a:ec:d6:9c:5e:14:e9:52:26:a6:4a:f8:6c:79:7b:
         7a:05:7d:1f:a9:7a:4f:64:8c:92:3e:aa:0d:4e:5e:f4:d8:34:
         66:52:d3:b6:8b:2c:84:12:e6:a2:91:7b:7b:b1:f2:ad:31:00:
         99:f3:8f:29:07:b4:12:0f:44:da:ea:f3:c6:a0:6e:1b:9e:24:
         e7:41:b6:27:41:62:3e:c4:09:02:11:62:34:6c:12:83:84:b7:
         1f:25:43:1b:bb:9e:29:dd:0b:f7:7c:0a:38:d3:98:1a:f3:0a:
         8b:30:87:07:ea:4a:bc:43:09:a1:9f:32:f6:e8:81:f8:71:57:
         72:9f:51:68:9a:89:f7:b1:e9:65:31:2f:fa:58:82:a7:63:d2:
         5f:37:0a:d2:bd:b9:49:cc:a3:17:a7:4b:a2:e2:b6:48:1d:f4:
         c4:0b:c9:f2:d1:39:f3:8f:a3:a9:0e:82:19:e7:14:f5:78:ef:
         94:08:ee:25:d0:ab:0d:e5:4f:b2:39:27:ec:e3:d7:1b:ef:17:
         65:4e:f3:a8:76:4d:99:75:f5:61:8e:07:e6:b8:04:27:8f:25:
         ad:4a:cf:dc:8f:11
-----BEGIN CERTIFICATE-----
MIIE3zCCA0egAwIBAgIJAOKFYvMwR+yQMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD
VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU2F1c2FsaXRv
MRUwEwYDVQQKDAxIZWFsdGhlRGF0YTExFzAVBgNVBAMMDkVyaWMgSGFhcywgRFZN
MSUwIwYJKoZIhvcNAQkBFhZlaGFhc0BoZWFsdGhlZGF0YTEub3JnMB4XDTIxMTAy
NzE3NDIwNFoXDTIyMTAyMjE3NDIwNFowgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQI
DApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYXVzYWxpdG8xFTATBgNVBAoMDEhlYWx0
aGVEYXRhMTEXMBUGA1UEAwwORXJpYyBIYWFzLCBEVk0xJTAjBgkqhkiG9w0BCQEW
FmVoYWFzQGhlYWx0aGVkYXRhMS5vcmcwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
ggGKAoIBgQDpKcSkoAM6sW21+vWTeIVOGx10MWasQy7VHid6zyqXABM+znfBnXen
yU0j1FTvPmRfODoODXVuPUwDomhCHh+bclW9KM2o563cxRKEvBnaHrsjw5yNmxO5
YjERbhtHdQeqktdw3VYERR9HoxLO3FkszR292HTB4xW3yWlV3gTkMQozPScJKH3b
G8Pqq6AYPJ7C4YBIlUSdBMVl3qneEfg7fuxiFfXofdTVm7rMiiG7X9z43Pfilqae
isfmtRxAlRwENXrH3OvODPyL0rTnG8CsbAXYVIMmddHe4ZF9plh95sj4pMThLtcJ
X/o9XHLjh7EmZygJHWEQq4PwFwZdmbcfhCmOr88H8BbUru/7V6zbsG1N1CWlndlb
VznL+3IMOr8kXaHcanqfckgFV4ErnfjFJq1OIaAmsMj85xMkjyXLycLL/u5n2o6B
g93/URfquOoSIGOCR25DaZzpw2k3s7oE9dMwEWXtfXgYth2bljy5tFH0GjpOkx07
j7ZT5HnxnlsCAwEAAaNAMD4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCBeAwIQYD
VR0RBBowGIIWd3d3LmhlYWx0aGVkYXRhaW5jLmNvbTANBgkqhkiG9w0BAQsFAAOC
AYEACuMUNq9ay++e5YC7QFO94rfzxGQnF3GklZNAXmIr7PVGdiGY2GTx/9RtHCwT
KzkL+yvKojeZ9dVK8wrGViRkO/jUyZ+KcWRokUjs59ncJGRMSSRxteCQujt4hf2+
/qV+f2sMQtErwPE30vbqRYUNNNBVEQpaQx/acJDUv9v7sjHiJDqXwQ+rzj95iHAI
aeEHqF/Ms22p2beZuqvIQKeMl+svVqHtiuzWnF4U6VImpkr4bHl7egV9H6l6T2SM
kj6qDU5e9Ng0ZlLTtosshBLmopF7e7HyrTEAmfOPKQe0Eg9E2urzxqBuG54k50G2
J0FiPsQJAhFiNGwSg4S3HyVDG7ueKd0L93wKONOYGvMKizCHB+pKvEMJoZ8y9uiB
+HFXcp9RaJqJ97HpZTEv+liCp2PSXzcK0r25ScyjF6dLouK2SB30xAvJ8tE584+j
qQ6CGecU9XjvlAjuJdCrDeVPsjkn7OPXG+8XZU7zqHZNmXX1YY4H5rgEJ48lrUrP
3I8R
-----END CERTIFICATE-----

10. Verify Signature using the public key or the X.509 Certificate

Alternatively:

  1. visit https://jwt.io.
  2. At the top of the page, select "RS256" for the algorithm.
  3. Paste the JWS value printed below into the “Encoded” field.
  4. The plaintext JWT will be displayed in the “Decoded:Payload” field.
  5. The X509 Cert above will appear in the "Verify Signature" box.
  6. If verified, a “Signature Verified” message will appear in the bottom left hand corner.
recd_jws
'eyJhbGciOiJSUzI1NiIsImt0eSI6IlJTIiwidHlwIjoiSldUIiwieDVjIjpbIk1JSUUzekNDQTBlZ0F3SUJBZ0lKQU9LRll2TXdSK3lRTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdOTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKVTJGMWMyRnNhWFJ2TVJVd0V3WURWUVFLREF4SVpXRnNkR2hsUkdGMFlURXhGekFWQmdOVkJBTU1Ea1Z5YVdNZ1NHRmhjeXdnUkZaTk1TVXdJd1lKS29aSWh2Y05BUWtCRmhabGFHRmhjMEJvWldGc2RHaGxaR0YwWVRFdWIzSm5NQjRYRFRJeE1UQXlOekUzTkRJd05Gb1hEVEl5TVRBeU1qRTNOREl3TkZvd2dZMHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbFRZWFZ6WVd4cGRHOHhGVEFUQmdOVkJBb01ERWhsWVd4MGFHVkVZWFJoTVRFWE1CVUdBMVVFQXd3T1JYSnBZeUJJWVdGekxDQkVWazB4SlRBakJna3Foa2lHOXcwQkNRRVdGbVZvWVdGelFHaGxZV3gwYUdWa1lYUmhNUzV2Y21jd2dnR2lNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUURwS2NTa29BTTZzVzIxK3ZXVGVJVk9HeDEwTVdhc1F5N1ZIaWQ2enlxWEFCTSt6bmZCblhlbnlVMGoxRlR2UG1SZk9Eb09EWFZ1UFV3RG9taENIaCtiY2xXOUtNMm81NjNjeFJLRXZCbmFIcnNqdzV5Tm14TzVZakVSYmh0SGRRZXFrdGR3M1ZZRVJSOUhveExPM0Zrc3pSMjkySFRCNHhXM3lXbFYzZ1RrTVFvelBTY0pLSDNiRzhQcXE2QVlQSjdDNFlCSWxVU2RCTVZsM3FuZUVmZzdmdXhpRmZYb2ZkVFZtN3JNaWlHN1g5ejQzUGZpbHFhZWlzZm10UnhBbFJ3RU5YckgzT3ZPRFB5TDByVG5HOENzYkFYWVZJTW1kZEhlNFpGOXBsaDk1c2o0cE1UaEx0Y0pYL285WEhMamg3RW1aeWdKSFdFUXE0UHdGd1pkbWJjZmhDbU9yODhIOEJiVXJ1LzdWNnpic0cxTjFDV2xuZGxiVnpuTCszSU1PcjhrWGFIY2FucWZja2dGVjRFcm5makZKcTFPSWFBbXNNajg1eE1ranlYTHljTEwvdTVuMm82Qmc5My9VUmZxdU9vU0lHT0NSMjVEYVp6cHcyazNzN29FOWRNd0VXWHRmWGdZdGgyYmxqeTV0RkgwR2pwT2t4MDdqN1pUNUhueG5sc0NBd0VBQWFOQU1ENHdEQVlEVlIwVEJBVXdBd0VCL3pBTEJnTlZIUThFQkFNQ0JlQXdJUVlEVlIwUkJCb3dHSUlXZDNkM0xtaGxZV3gwYUdWa1lYUmhhVzVqTG1OdmJUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FZRUFDdU1VTnE5YXkrK2U1WUM3UUZPOTRyZnp4R1FuRjNHa2xaTkFYbUlyN1BWR2RpR1kyR1R4LzlSdEhDd1RLemtMK3l2S29qZVo5ZFZLOHdyR1ZpUmtPL2pVeVorS2NXUm9rVWpzNTluY0pHUk1TU1J4dGVDUXVqdDRoZjIrL3FWK2Yyc01RdEVyd1BFMzB2YnFSWVVOTk5CVkVRcGFReC9hY0pEVXY5djdzakhpSkRxWHdRK3J6ajk1aUhBSWFlRUhxRi9NczIycDJiZVp1cXZJUUtlTWwrc3ZWcUh0aXV6V25GNFU2VkltcGtyNGJIbDdlZ1Y5SDZsNlQyU01rajZxRFU1ZTlOZzBabExUdG9zc2hCTG1vcEY3ZTdIeXJURUFtZk9QS1FlMEVnOUUydXJ6eHFCdUc1NGs1MEcySjBGaVBzUUpBaEZpTkd3U2c0UzNIeVZERzd1ZUtkMEw5M3dLT05PWUd2TUtpekNIQitwS3ZFTUpvWjh5OXVpQitIRlhjcDlSYUpxSjk3SHBaVEV2K2xpQ3AyUFNYemNLMHIyNVNjeWpGNmRMb3VLMlNCMzB4QXZKOHRFNTg0K2pxUTZDR2VjVTlYanZsQWp1SmRDckRlVlBzamtuN09QWEcrOFhaVTd6cUhaTm1YWDFZWTRINXJnRUo0OGxyVXJQM0k4UiJdfQ.eyJlbnRyeSI6W3siZnVsbFVybCI6Imh0dHA6Ly9oYXBpLmZoaXIub3JnL2Jhc2VSNC9Db25kaXRpb24vNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwicmVzb3VyY2UiOnsiY2F0ZWdvcnkiOlt7ImNvZGluZyI6W3siY29kZSI6ImVuY291bnRlci1kaWFnbm9zaXMiLCJkaXNwbGF5IjoiRW5jb3VudGVyIERpYWdub3NpcyIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNhdGVnb3J5In1dfV0sImNsaW5pY2FsU3RhdHVzIjp7ImNvZGluZyI6W3siY29kZSI6ImFjdGl2ZSIsInN5c3RlbSI6Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsIn1dfSwiY29kZSI6eyJjb2RpbmciOlt7ImNvZGUiOiIxMjM0IiwiZGlzcGxheSI6IkV4YW1wbGl0aXMiLCJzeXN0ZW0iOiJodHRwOi8vc25vbWVkLmluZm8vc2N0In1dLCJ0ZXh0IjoiRXhhbXBsaXRpcyJ9LCJlbmNvdW50ZXIiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9FbmNvdW50ZXIvNWZlNjJjZDUtYmZjZi00ZDNiLWExZTktODBkNmY3NWQ2ZjgyIn0sImlkIjoiNGFjNDE3MTUtZmNiZC00MjFjLTg3OTYtOWIyYzk3MDZkZDNmIiwibWV0YSI6eyJsYXN0VXBkYXRlZCI6IjIwMjAtMDQtMjhUMjA6Mjg6MDAuMDA4KzAwOjAwIiwicHJvZmlsZSI6WyJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWNvbmRpdGlvbiJdLCJzb3VyY2UiOiIjY2FiaUpJSzUxc0QyaXo0TiIsInZlcnNpb25JZCI6IjEwIn0sIm9uc2V0RGF0ZVRpbWUiOiIyMDE4LTEwLTIxVDIxOjIyOjE1LTA3OjAwIiwicmVjb3JkZWREYXRlIjoiMjAxOC0xMC0yMVQyMToyMjoxNS0wNzowMCIsInJlc291cmNlVHlwZSI6IkNvbmRpdGlvbiIsInN1YmplY3QiOnsicmVmZXJlbmNlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9QYXRpZW50LzA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiJ9LCJ2ZXJpZmljYXRpb25TdGF0dXMiOnsiY29kaW5nIjpbeyJjb2RlIjoiY29uZmlybWVkIiwic3lzdGVtIjoiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tdmVyLXN0YXR1cyJ9XX19LCJzZWFyY2giOnsibW9kZSI6Im1hdGNoIn19XSwibGluayI6W3sicmVsYXRpb24iOiJzZWxmIiwidXJsIjoiaHR0cDovL2hhcGkuZmhpci5vcmcvYmFzZVI0L0NvbmRpdGlvbj9wYXRpZW50PTA2ZTFmMGRkLTVmYmUtNDQ4MC05YmI0LTZiNTRlYzAyZDMxYiZjbGluaWNhbC1zdGF0dXM9YWN0aXZlLHJlY3VycmFuY2UscmVtaXNzaW9uIn1dLCJyZXNvdXJjZVR5cGUiOiJCdW5kbGUiLCJ0b3RhbCI6MSwidHlwZSI6InNlYXJjaHNldCJ9.rE5gfhbdbkmuYpKPb87Tpg1nRdRJTyNfWAOmXR2AA63FGKz2br_aAFtXj2UwIVHjp_AmgOZrpKmUFZnibBRcRTBUVfPWVrgRHleP1Dd1QL2qiaz0Pxw38odIVKQw88pFXBfIASsnrFMEJ_x07XQLWaXLOcEYVBb2s1SoULIW_McJCeCVKEnhrrsmNhXvAHLxYm2y8lUdTYYMH7rCM2FEIglavxLA4QI1nPv5i5ZwSOZDI1OoPtK4dHbbvggsKnAtBJL890AJ7JD9h1QTmw8vSWjydCBNilgGiyffPmfSGXFvGQ9tEcETHW0NlUEF649Zt-k71aMhkjooCsRwM9Dmb4STqO7Fy0MzWbvdi6K46W_rf77gv75Xr_cQyvSubWumRTMompgG1qOz5LbClfKHoWWyIzBxM2u7wqBukJTAIm10DSDgFg79-5oLPIDOj19VYsnLcyYFdYIx2G3gkBhcbExDdytlKGIZ8YvIEojDfNibC3wLWwNvmptBww-hPdT7'
!openssl x509 -in recd_cert.der -inform DER -pubkey -noout > recd-public-key.pem
with open('recd-public-key.pem') as f:
    pem_public = f.read()
pem_public
'-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA6SnEpKADOrFttfr1k3iF\nThsddDFmrEMu1R4nes8qlwATPs53wZ13p8lNI9RU7z5kXzg6Dg11bj1MA6JoQh4f\nm3JVvSjNqOet3MUShLwZ2h67I8OcjZsTuWIxEW4bR3UHqpLXcN1WBEUfR6MSztxZ\nLM0dvdh0weMVt8lpVd4E5DEKMz0nCSh92xvD6qugGDyewuGASJVEnQTFZd6p3hH4\nO37sYhX16H3U1Zu6zIohu1/c+Nz34pamnorH5rUcQJUcBDV6x9zrzgz8i9K05xvA\nrGwF2FSDJnXR3uGRfaZYfebI+KTE4S7XCV/6PVxy44exJmcoCR1hEKuD8BcGXZm3\nH4Qpjq/PB/AW1K7v+1es27BtTdQlpZ3ZW1c5y/tyDDq/JF2h3Gp6n3JIBVeBK534\nxSatTiGgJrDI/OcTJI8ly8nCy/7uZ9qOgYPd/1EX6rjqEiBjgkduQ2mc6cNpN7O6\nBPXTMBFl7X14GLYdm5Y8ubRR9Bo6TpMdO4+2U+R58Z5bAgMBAAE=\n-----END PUBLIC KEY-----\n'
try:
    verify = jws.verify(recd_jws, pem_public , algorithms=['RS256'])
    print('#     #                                                ### ')
    print('#     #  ######  #####   #  ######  #  ######  #####   ### ')
    print('#     #  #       #    #  #  #       #  #       #    #  ### ')
    print('#     #  #####   #    #  #  #####   #  #####   #    #   #  ')
    print(' #   #   #       #####   #  #       #  #       #    #      ')
    print('  # #    #       #   #   #  #       #  #       #    #  ### ')
    print('   #     ######  #    #  #  #       #  ######  #####   ### ')
    print('                                                           ')

except Exception as e:
    print('#    #   ####   #####      #    #  ######  #####   #  ######  #  ######  #####       ###          #   ')
    print('##   #  #    #    #        #    #  #       #    #  #  #       #  #       #    #       #          #    ')
    print('# #  #  #    #    #        #    #  #####   #    #  #  #####   #  #####   #    #           #####  #    ')
    print('#  # #  #    #    #        #    #  #       #####   #  #       #  #       #    #       #          #    ')
    print('#   ##  #    #    #         #  #   #       #   #   #  #       #  #       #    #      ###          #   ')
    print('#    #   ####     #          ##    ######  #    #  #  #       #  ######  #####        #            ## ')
    print('                ')
    print(f"not verified: {e}")
#     #                                                ###
#     #  ######  #####   #  ######  #  ######  #####   ###
#     #  #       #    #  #  #       #  #       #    #  ###
#     #  #####   #    #  #  #####   #  #####   #    #   #  
 #   #   #       #####   #  #       #  #       #    #      
  # #    #       #   #   #  #       #  #       #    #  ###
   #     ######  #    #  #  #       #  ######  #####   ###