...
draw.io Diagram | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Generation of ES256 asymmetric keys
openssl command can be used to generate ES256 asymmetric keys as required by the FAPI part 2 specification. Office will keep the private key and share the public key for registration with WIPO. Below is an example of script to generate ES256 asymmetric keys.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
#!/bin/bash
# Set the environment
PRIVATE_KEY_ES256=es256_private.pem
PUBLIC_KEY_ES256=es256_public.pem
CLIENT_NAME=DAS
# Generates the ES256 keys
openssl ecparam -genkey -name prime256v1 -noout -out "${PRIVATE_KEY_ES256}"
# Extracts the public key
openssl ec -in "${PRIVATE_KEY_ES256}" -pubout -out "${PUBLIC_KEY_ES256}"
# Generates an x509 certificate
CERT_KEY_ES256=es256_cert.pem
OPENSSL_CONF=./openssl.cnf
CERT_CN="${CLIENT_NAME} private_key_jwt authentication"
# Build the certificate config file
printf '[ req ]\n' > "${OPENSSL_CONF}"
printf 'prompt = no\n' >> "${OPENSSL_CONF}"
printf 'distinguished_name = req_distinguished_name\n' >> "${OPENSSL_CONF}"
printf '[ req_distinguished_name ]\n' >> "${OPENSSL_CONF}"
printf 'CN = %s\n' "${CERT_CN}" >> "${OPENSSL_CONF}"
# Creates the x509 certificate
openssl req -x509 -new -config "${OPENSSL_CONF}" -key "${PRIVATE_KEY_ES256}" -out "${CERT_KEY_ES256}" |
...
Files
...
Description
...
private_key_jwt authentication process
The authentication flow of private_key_jwt is depicted in the diagram below
...
The private_key_jwt authentication consists of creating a JSON structure containing the following login attributes:
...
Attribute
...
Example
...
Description
...
ES256 signing algorithm + above attributes + signature of them must be served in JWT format (rfc7519), see below:
JWT client assertion header
...
{
"alg"
:
"ES256"
,
"typ"
:
"JWT"
}
...
{
"iss"
:
"das-api-auth"
,
"sub"
:
"das-api-auth"
,
"aud"
:
"https://logindev.wipo.int:443/am/oauth2/access_token"
,
"exp"
: 1622450728
}
...
# Signature of the header and payload sections. # it is an array of bytes encoded in base 64
All parts are encoded and separated by '.' to make up the JWT as follows
private_key_jwt assertion
...
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkYXMtYXBpLWF1dGgiLCJzdWIiOiJkYXMtYXBpLWF1dGgiLCJhdWQiOiJodHRwczovL2xvZ2luZGV2LndpcG8uaW50OjQ0My9hbS9vYXV0aDIvYWNjZXNzX3Rva2VuIiwiZXhwIjoxNjIyNDUwNzI4fQ.BLA6k2kKKFVm6AG-DPDpRU_5JDFGRF1dHjKul7saWCv5OxXGg4EY-J9e1p8Dg0ngD2dZ2grkJ2su7jaHy67YEw
The JWT client assertion can now be submitted to authorization server for authentication with the token endpoint (i.e. POST https://logindev.wipo.int:443/am/oauth2/access_token in the attached specification) including the following parameters for client_credentials:
...
POST parameter
...
example
...
Description
...
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkYXMtYXBpLWF1dG giLCJzdWIiOiJkYXMtYXBpLWF1dGgiLCJhdWQiOiJodHRwczovL2xvZ2luZG V2LndpcG8uaW50OjQ0My9hbS9vYXV0aDIvYWNjZXNzX3Rva2VuIiwiZXh wIjoxNjIyNDUwNzI4fQ.BLA6k2kKKFVm6AG-DPDpRU_5JDFGRF1dHjKul7saWCv5OxXGg4EY-J9e1p8Dg0ngD2dZ2grkJ2su7jaHy67YEw
...
Note |
---|
The above token endpoint is part of third party product that supports OpenID Connect (OIDC) authentication protocol based on the OAuth 2.0 family of specifications |
Below is an example of authentication script.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
#!/bin/bash
PRIVATE_KEY_ES256=es256_private.pem
CLIENT_ID=das-api-auth
SCOPE="das-api/das-access"
ISSUER="https://logindev.wipo.int/am/oauth2"
# https://logindev.wipo.int/am/oauth2/.well-known/openid-configuration
OIDC_CONFIG_JSON=$(curl -k "${ISSUER}/.well-known/openid-configuration")
# Generic way to obtain the token endpoint
TOKEN_ENDPOINT=$(printf '%s' ${OIDC_CONFIG_JSON} | jq -r ".token_endpoint")
UTC_TIME=$(date -u +%s)
EXP_TIME=$(expr "$UTC_TIME" + 10)
JSON='{'
JSON=${JSON}$(printf '"iss":"%s"' ${CLIENT_ID})
JSON=${JSON}$(printf ',"sub":"%s"' ${CLIENT_ID})
JSON=${JSON}$(printf ',"aud":"%s"' ${TOKEN_ENDPOINT})
JSON=${JSON}$(printf ',"exp":%s' ${EXP_TIME})
JSON=${JSON}'}'
JSON_HEADER_B64=$(printf '{"alg":"ES256","typ":"JWT"}' | jq -cj | base64 -w0 | tr -d '\n=' | tr '+/' '-_')
JSON_PAYLOAD_B64=$(printf $JSON | jq -cj | base64 -w0 | tr -d '\n=' | tr '+/' '-_')
JSON_SIGNATURE_ASN1_B64=$(printf '%s.%s' $JSON_HEADER_B64 $JSON_PAYLOAD_B64 | openssl dgst -sha256 -sign "${PRIVATE_KEY_ES256}" | openssl asn1parse -inform DER | base64 -w0)
JSON_SIGNATURE_HEX=$(printf $JSON_SIGNATURE_ASN1_B64 | base64 -d | sed -n '/INTEGER/p' | sed 's/.*INTEGER\s*://g' | sed -z 's/[^0-9A-F]//g')
JSON_SIGNATURE_B64=$(printf $JSON_SIGNATURE_HEX | xxd -p -r | base64 -w0 | tr -d '\n=' | tr '+/' '-_')
JWT_ASSERTION=$(printf '%s.%s.%s' $JSON_HEADER_B64 $JSON_PAYLOAD_B64 $JSON_SIGNATURE_B64)
# echo $JWT_ASSERTION
# Access token private_key_jwt
curl --insecure \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode scope="${SCOPE}" \
--data-urlencode "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
--data-urlencode "client_assertion=${JWT_ASSERTION}" \
--url "${TOKEN_ENDPOINT}" |
The output of the script is as follows:
private_key_jwt authentication output
...
{
"access_token"
:
"eyJ0eXAiOiJKV1QiLCJraWQiOiJmVWRmbEJSa3c5bm1tejcrL3BmMWM5d2RYdXc9IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJkYXMtYXBpLWF1dGgiLCJjdHMiOiJPQVVUSDJfU1RBVEVMRVNTX0dSQU5UIiwiYXVkaXRUcmFja2luZ0lkIjoiMTQyYjMwODEtZDNjNy00MjJjLWI4ZDQtNjU4NjkwNjVmMzQ4LTU0OTkxIiwiaXNzIjoiaHR0cHM6Ly9sb2dpbmRldi53aXBvLmludDo0NDMvYW0vb2F1dGgyIiwidG9rZW5OYW1lIjoiYWNjZXNzX3Rva2VuIiwidG9rZW5fdHlwZSI6IkJlYXJlciIsImF1dGhHcmFudElkIjoibko4bmh5bEM4S3g5RFk4bDJTSGxvcHdDZmJnIiwiYXVkIjoiZGFzLWFwaS1hdXRoIiwibmJmIjoxNjIyNDU0OTUzLCJncmFudF90eXBlIjoiY2xpZW50X2NyZWRlbnRpYWxzIiwic2NvcGUiOlsiZGFzLWFwaS9kYXMtYWNjZXNzIl0sImF1dGhfdGltZSI6MTYyMjQ1NDk1MywicmVhbG0iOiIvIiwiZXhwIjoxNjIyNDU4NTUzLCJpYXQiOjE2MjI0NTQ5NTMsImV4cGlyZXNfaW4iOjM2MDAsImp0aSI6InJvRzhtcWE4WjFaM0YwME1kMjB2VW95aEEwSSJ9.d1EEdioprD2AxQxQcVj0zlN8hvSaIdtub0Lk887m52qEKFt9YiW3uGhpw8bMnhwsUyBbbdFq1flA3pVdKYAdNhQ2dRBIemTH8_NjA4l4giGpLeKJ7WRQA-ldsWrrLkLkVu7gbx7TmMLrTkXgL17kiLdPQ44S1O6LKX52v3KkT0XYEyMYIuzYlnMBs1GQWkoJEALZVIH3TtaAG22o4dxlCcMVxUCo-SyOctjRkfmLvuKEXpDvAG2F93o61Mz1sOtSC2m6nBQA9zd3MxtNd5vd0791QH16Of53IozPj7jRXblYCYq9SJyXzdHN7IEJWrT7C1vvwFVnq8c8QArKsMmgBw"
,
"scope"
:
"das-api/das-access"
,
"token_type"
:
"Bearer"
,
"expires_in"
: 3599
}
Access_token attributes like signature, validity, audience and scopes must be verified by the client, similarly DAS API must also verify the access_token and must additionally check if the client id (=sub claim) is authorized. DAS API must maintain the whitelisted clients
...
Children Display |
---|
...
Children Display | ||
---|---|---|
|
...