feat(oidc): issuer jwk certificates (#3989)
This allows for JWKs to include certificate information, either signed via Global PKI, Enterprise PKI, or self-signed.pull/3855/head
parent
66ea374227
commit
6810c91d34
|
@ -815,35 +815,78 @@ notifier:
|
|||
## HMAC Secret can also be set using a secret: https://www.authelia.com/c/secrets
|
||||
# hmac_secret: this_is_a_secret_abc123abc123abc
|
||||
|
||||
## The issuer_certificate_chain is an optional PEM encoded certificate chain. It's used in conjunction with the
|
||||
## issuer_private_key to sign JWT's. All certificates in the chain must be within the validity period, and every
|
||||
## certificate included must be signed by the certificate immediately after it if provided.
|
||||
# issuer_certificate_chain: |
|
||||
# -----BEGIN CERTIFICATE-----
|
||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
||||
# -----END CERTIFICATE-----
|
||||
# -----BEGIN CERTIFICATE-----
|
||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
||||
# qocikt3WAdU^invalid DO NOT USE=
|
||||
# -----END CERTIFICATE-----
|
||||
|
||||
## The issuer_private_key is used to sign the JWT forged by OpenID Connect.
|
||||
## Issuer Private Key can also be set using a secret: https://www.authelia.com/c/secrets
|
||||
# issuer_private_key: |
|
||||
# -----BEGIN RSA PRIVATE KEY-----
|
||||
# MXIEogIB$AKCAQEAxZVJP3WF//PG2fLQoEC9DtdiFG/+00vqlbVzz47nyxKONIPI
|
||||
# lmL3UdmqpGTKMe/5Brqse4ZAKlQHiDbwzK9ypnfigtHuvh/JO0S7ChP70RC67ed1
|
||||
# HV1nyfz5eW3llbtGJPrlYLqITNgctHp6zmRUFtSzPj9qFvozI93LJi492yL1+vu8
|
||||
# Un3Dm8+Qq6XM2tPdEcldB/dtBwOWoF+8eOOVsu0TDuB5bwlhBVGJuSAuzBPRS2bF
|
||||
# Ga4uk0JDdkDOMCEQxC5uWDFxgfERSMFyfLVWD47woDbuWEBq10c0z+dpWPMp7Ain
|
||||
# YnnkqicwCN88Z0zid6MmMQ65F4+9Hc+qC/p6xwIDAQABAoIBAGlhaAHKor+Su3o/
|
||||
# AXqXTL5/rbYMzbLQiLt0XeJT69jpeqMTroZXHmWvXE3128mqnf0yzw/K2Ko6yxGh
|
||||
# i+j/onya8FqpsVYCCgfsbn2/js1AyRJeIp6Y1ORsYnqbXJnxmkXa80AV/OBPW2/+
|
||||
# 60TtSdQrebY3iFPc+i2k+9bPTvpyyDLKlz8UwdZG+k5uyYNIyQTccz+PjwsIvDij
|
||||
# 7tKYamhhLN3QXt3/aZTFpjTgezP4WyriZxjWrddHowc47q2rwNS95ND39JcysJAc
|
||||
# 0Pcbu8A5lVa7Fx33uOtzDfKWIW7xVEN+OtPgN+FbTjXcXk5IZedl+pW5lU5P++G/
|
||||
# ZPvz+WECgYEA9g6HwdODW3e68bOqsFoKg35+vfUFMzlyMF8HFylNVfnLpTEDr637
|
||||
# owzMFvcUxVd71b+gV5nnnbI+riUFIgyR8vhCjhy4moopDPahC4/KwN4NG6uz+i1h
|
||||
# AB6D5+zn2BjnO/5xMMFGlApWtRNmJVGYlNDj3bXKh2VXzzy03VNeD8kCgYEAzZFL
|
||||
# OlzoRB1HKpTWIECcuvxofMxLOLb3zs0k2t/FYNYIpovmGWCCAULz13y53e5+/+5m
|
||||
# 7I9VUZJFaIhaZ36qVBApCKdru69pZMkWCcQO9jELFcx51Ez7OgJWzu7GS1QJCPKC
|
||||
# fEDxI0rZK21j93/Sl/nUnEir7CYpQ+wvCaGuHg8CgYAXgbncfY1+DokwkB6NbHy2
|
||||
# pT4Mfbz6cNGE538w6kQ2I4AeDvmwLentYMqaow478CinegAiflSPTzkHwAemghbr
|
||||
# ZGZPV1UXhn13fJRUG2+eT1hnPVcbXnx223N0k8Bud6qXo65CnyRT/kzcTbcjd5Eh
|
||||
# Hne2daicmMTzynPo9Q72aQKBgBmobO9X8VWvIdbaxO85oVZlctVA2pK1o7CYQmVf
|
||||
# UM+JZ4MCKzI3rYJizPS0iK5+ujNPmmEkcs2/qBIoEsCgOrpLWhPOcc/3UPxXbPzD
|
||||
# D+sCrBOIdhxdj23qJNOnUfDNCGOpgUfpAzAYg4q8GKInvi1h7XukRnEvQi9MJ4LY
|
||||
# P1dZAoGASGcGnTMkmeSXP8ux+dvQJAiJskn/sJIgBZ5uq5GRCeLBUosRSVxM75UK
|
||||
# vAh/c/RBj+pYXVKuPuHGZCQJxsdcRXzXNGouUtgbaYML5Me/Hagt20QzDRBfuGBg
|
||||
# qeZBJaXhjElvw6PUWtg4x+LYRCBpq/bS3LK3ozZrSTukVkKDegw=
|
||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
||||
# DO NOT USE==
|
||||
# -----END RSA PRIVATE KEY-----
|
||||
|
||||
## The lifespans configure the expiration for these token types.
|
||||
|
|
|
@ -33,33 +33,72 @@ The following snippet provides a sample-configuration for the OIDC identity prov
|
|||
identity_providers:
|
||||
oidc:
|
||||
hmac_secret: this_is_a_secret_abc123abc123abc
|
||||
issuer_certificate_chain: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
||||
/Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
||||
LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
||||
91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
||||
kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
||||
Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||
AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
||||
/ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
||||
lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
||||
wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
||||
OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
||||
ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
||||
zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
||||
5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
||||
kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
||||
ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
||||
Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
||||
AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
||||
Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
||||
kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
||||
71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
||||
HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
||||
D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
||||
2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
||||
qocikt3WAdU^invalid DO NOT USE=
|
||||
-----END CERTIFICATE-----
|
||||
issuer_private_key: |
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MXIEogIB$AKCAQEAxZVJP3WF//PG2fLQoEC9DtdiFG/+00vqlbVzz47nyxKONIPI
|
||||
lmL3UdmqpGTKMe/5Brqse4ZAKlQHiDbwzK9ypnfigtHuvh/JO0S7ChP70RC67ed1
|
||||
HV1nyfz5eW3llbtGJPrlYLqITNgctHp6zmRUFtSzPj9qFvozI93LJi492yL1+vu8
|
||||
Un3Dm8+Qq6XM2tPdEcldB/dtBwOWoF+8eOOVsu0TDuB5bwlhBVGJuSAuzBPRS2bF
|
||||
Ga4uk0JDdkDOMCEQxC5uWDFxgfERSMFyfLVWD47woDbuWEBq10c0z+dpWPMp7Ain
|
||||
YnnkqicwCN88Z0zid6MmMQ65F4+9Hc+qC/p6xwIDAQABAoIBAGlhaAHKor+Su3o/
|
||||
AXqXTL5/rbYMzbLQiLt0XeJT69jpeqMTroZXHmWvXE3128mqnf0yzw/K2Ko6yxGh
|
||||
i+j/onya8FqpsVYCCgfsbn2/js1AyRJeIp6Y1ORsYnqbXJnxmkXa80AV/OBPW2/+
|
||||
60TtSdQrebY3iFPc+i2k+9bPTvpyyDLKlz8UwdZG+k5uyYNIyQTccz+PjwsIvDij
|
||||
7tKYamhhLN3QXt3/aZTFpjTgezP4WyriZxjWrddHowc47q2rwNS95ND39JcysJAc
|
||||
0Pcbu8A5lVa7Fx33uOtzDfKWIW7xVEN+OtPgN+FbTjXcXk5IZedl+pW5lU5P++G/
|
||||
ZPvz+WECgYEA9g6HwdODW3e68bOqsFoKg35+vfUFMzlyMF8HFylNVfnLpTEDr637
|
||||
owzMFvcUxVd71b+gV5nnnbI+riUFIgyR8vhCjhy4moopDPahC4/KwN4NG6uz+i1h
|
||||
AB6D5+zn2BjnO/5xMMFGlApWtRNmJVGYlNDj3bXKh2VXzzy03VNeD8kCgYEAzZFL
|
||||
OlzoRB1HKpTWIECcuvxofMxLOLb3zs0k2t/FYNYIpovmGWCCAULz13y53e5+/+5m
|
||||
7I9VUZJFaIhaZ36qVBApCKdru69pZMkWCcQO9jELFcx51Ez7OgJWzu7GS1QJCPKC
|
||||
fEDxI0rZK21j93/Sl/nUnEir7CYpQ+wvCaGuHg8CgYAXgbncfY1+DokwkB6NbHy2
|
||||
pT4Mfbz6cNGE538w6kQ2I4AeDvmwLentYMqaow478CinegAiflSPTzkHwAemghbr
|
||||
ZGZPV1UXhn13fJRUG2+eT1hnPVcbXnx223N0k8Bud6qXo65CnyRT/kzcTbcjd5Eh
|
||||
Hne2daicmMTzynPo9Q72aQKBgBmobO9X8VWvIdbaxO85oVZlctVA2pK1o7CYQmVf
|
||||
UM+JZ4MCKzI3rYJizPS0iK5+ujNPmmEkcs2/qBIoEsCgOrpLWhPOcc/3UPxXbPzD
|
||||
D+sCrBOIdhxdj23qJNOnUfDNCGOpgUfpAzAYg4q8GKInvi1h7XukRnEvQi9MJ4LY
|
||||
P1dZAoGASGcGnTMkmeSXP8ux+dvQJAiJskn/sJIgBZ5uq5GRCeLBUosRSVxM75UK
|
||||
vAh/c/RBj+pYXVKuPuHGZCQJxsdcRXzXNGouUtgbaYML5Me/Hagt20QzDRBfuGBg
|
||||
qeZBJaXhjElvw6PUWtg4x+LYRCBpq/bS3LK3ozZrSTukVkKDegw=
|
||||
MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
||||
T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
||||
KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
||||
+5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
||||
LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
||||
txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
||||
aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
||||
Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
||||
ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
||||
LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
||||
jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
||||
BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
||||
Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
||||
R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
||||
tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
||||
ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
||||
lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
||||
6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
||||
fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
||||
9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
||||
jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
||||
rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
||||
n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
||||
yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
||||
27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
||||
DO NOT USE==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
access_token_lifespan: 1h
|
||||
authorize_code_lifespan: 1m
|
||||
|
@ -120,6 +159,23 @@ It's __strongly recommended__ this is a
|
|||
[Random Alphanumeric String](../miscellaneous/guides.md#generating-a-random-alphanumeric-string) with 64 or more
|
||||
characters.
|
||||
|
||||
### issuer_certificate_chain
|
||||
|
||||
{{< confkey type="string" required="no" >}}
|
||||
|
||||
The certificate chain/bundle to be used with the [issuer_private_key](#issuer_private_key) DER base64 ([RFC4648])
|
||||
encoded PEM format used to sign/encrypt the [OpenID Connect] [JWT]'s. When configured it enables the [x5c] and [x5t]
|
||||
JSON key's in the JWKs [Discoverable Endpoint](../../integration/openid-connect/introduction.md#discoverable-endpoints)
|
||||
as per [RFC7517].
|
||||
|
||||
[RFC7517]: https://www.rfc-editor.org/rfc/rfc7517
|
||||
[x5c]: https://www.rfc-editor.org/rfc/rfc7517#section-4.7
|
||||
[x5t]: https://www.rfc-editor.org/rfc/rfc7517#section-4.8
|
||||
|
||||
The first certificate in the chain must have the public key for the [issuer_private_key](#issuer_private_key), each
|
||||
certificate in the chain must be valid for the current date, and each certificate in the chain should be signed by the
|
||||
certificate immediately following it if present.
|
||||
|
||||
### issuer_private_key
|
||||
|
||||
{{< confkey type="string" required="yes" >}}
|
||||
|
@ -127,10 +183,13 @@ characters.
|
|||
*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__
|
||||
especially for containerized deployments.*
|
||||
|
||||
The private key in DER base64 ([RFC4648]) encoded PEM format used to encrypt the [OpenID Connect] [JWT]'s. The key must
|
||||
be generated by the administrator and can be done by following the
|
||||
The private key in DER base64 ([RFC4648]) encoded PEM format used to sign/encrypt the [OpenID Connect] issued [JWT]'s.
|
||||
The key must be generated by the administrator and can be done by following the
|
||||
[Generating an RSA Keypair](../miscellaneous/guides.md#generating-an-rsa-keypair) guide.
|
||||
|
||||
If the [issuer_certificate_chain](#issuer_certificate_chain) is provided the private key must include matching public
|
||||
key data for the first certificate in the chain.
|
||||
|
||||
### access_token_lifespan
|
||||
|
||||
{{< confkey type="duration" default="1h" required="no" >}}
|
||||
|
|
|
@ -815,35 +815,78 @@ notifier:
|
|||
## HMAC Secret can also be set using a secret: https://www.authelia.com/c/secrets
|
||||
# hmac_secret: this_is_a_secret_abc123abc123abc
|
||||
|
||||
## The issuer_certificate_chain is an optional PEM encoded certificate chain. It's used in conjunction with the
|
||||
## issuer_private_key to sign JWT's. All certificates in the chain must be within the validity period, and every
|
||||
## certificate included must be signed by the certificate immediately after it if provided.
|
||||
# issuer_certificate_chain: |
|
||||
# -----BEGIN CERTIFICATE-----
|
||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
||||
# -----END CERTIFICATE-----
|
||||
# -----BEGIN CERTIFICATE-----
|
||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
||||
# qocikt3WAdU^invalid DO NOT USE=
|
||||
# -----END CERTIFICATE-----
|
||||
|
||||
## The issuer_private_key is used to sign the JWT forged by OpenID Connect.
|
||||
## Issuer Private Key can also be set using a secret: https://www.authelia.com/c/secrets
|
||||
# issuer_private_key: |
|
||||
# -----BEGIN RSA PRIVATE KEY-----
|
||||
# MXIEogIB$AKCAQEAxZVJP3WF//PG2fLQoEC9DtdiFG/+00vqlbVzz47nyxKONIPI
|
||||
# lmL3UdmqpGTKMe/5Brqse4ZAKlQHiDbwzK9ypnfigtHuvh/JO0S7ChP70RC67ed1
|
||||
# HV1nyfz5eW3llbtGJPrlYLqITNgctHp6zmRUFtSzPj9qFvozI93LJi492yL1+vu8
|
||||
# Un3Dm8+Qq6XM2tPdEcldB/dtBwOWoF+8eOOVsu0TDuB5bwlhBVGJuSAuzBPRS2bF
|
||||
# Ga4uk0JDdkDOMCEQxC5uWDFxgfERSMFyfLVWD47woDbuWEBq10c0z+dpWPMp7Ain
|
||||
# YnnkqicwCN88Z0zid6MmMQ65F4+9Hc+qC/p6xwIDAQABAoIBAGlhaAHKor+Su3o/
|
||||
# AXqXTL5/rbYMzbLQiLt0XeJT69jpeqMTroZXHmWvXE3128mqnf0yzw/K2Ko6yxGh
|
||||
# i+j/onya8FqpsVYCCgfsbn2/js1AyRJeIp6Y1ORsYnqbXJnxmkXa80AV/OBPW2/+
|
||||
# 60TtSdQrebY3iFPc+i2k+9bPTvpyyDLKlz8UwdZG+k5uyYNIyQTccz+PjwsIvDij
|
||||
# 7tKYamhhLN3QXt3/aZTFpjTgezP4WyriZxjWrddHowc47q2rwNS95ND39JcysJAc
|
||||
# 0Pcbu8A5lVa7Fx33uOtzDfKWIW7xVEN+OtPgN+FbTjXcXk5IZedl+pW5lU5P++G/
|
||||
# ZPvz+WECgYEA9g6HwdODW3e68bOqsFoKg35+vfUFMzlyMF8HFylNVfnLpTEDr637
|
||||
# owzMFvcUxVd71b+gV5nnnbI+riUFIgyR8vhCjhy4moopDPahC4/KwN4NG6uz+i1h
|
||||
# AB6D5+zn2BjnO/5xMMFGlApWtRNmJVGYlNDj3bXKh2VXzzy03VNeD8kCgYEAzZFL
|
||||
# OlzoRB1HKpTWIECcuvxofMxLOLb3zs0k2t/FYNYIpovmGWCCAULz13y53e5+/+5m
|
||||
# 7I9VUZJFaIhaZ36qVBApCKdru69pZMkWCcQO9jELFcx51Ez7OgJWzu7GS1QJCPKC
|
||||
# fEDxI0rZK21j93/Sl/nUnEir7CYpQ+wvCaGuHg8CgYAXgbncfY1+DokwkB6NbHy2
|
||||
# pT4Mfbz6cNGE538w6kQ2I4AeDvmwLentYMqaow478CinegAiflSPTzkHwAemghbr
|
||||
# ZGZPV1UXhn13fJRUG2+eT1hnPVcbXnx223N0k8Bud6qXo65CnyRT/kzcTbcjd5Eh
|
||||
# Hne2daicmMTzynPo9Q72aQKBgBmobO9X8VWvIdbaxO85oVZlctVA2pK1o7CYQmVf
|
||||
# UM+JZ4MCKzI3rYJizPS0iK5+ujNPmmEkcs2/qBIoEsCgOrpLWhPOcc/3UPxXbPzD
|
||||
# D+sCrBOIdhxdj23qJNOnUfDNCGOpgUfpAzAYg4q8GKInvi1h7XukRnEvQi9MJ4LY
|
||||
# P1dZAoGASGcGnTMkmeSXP8ux+dvQJAiJskn/sJIgBZ5uq5GRCeLBUosRSVxM75UK
|
||||
# vAh/c/RBj+pYXVKuPuHGZCQJxsdcRXzXNGouUtgbaYML5Me/Hagt20QzDRBfuGBg
|
||||
# qeZBJaXhjElvw6PUWtg4x+LYRCBpq/bS3LK3ozZrSTukVkKDegw=
|
||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
||||
# DO NOT USE==
|
||||
# -----END RSA PRIVATE KEY-----
|
||||
|
||||
## The lifespans configure the expiration for these token types.
|
||||
|
|
|
@ -31,8 +31,9 @@ const (
|
|||
errFmtSecretIOIssue = "secrets: error loading secret path %s into key '%s': %v"
|
||||
errFmtGenerateConfiguration = "error occurred generating configuration: %+v"
|
||||
|
||||
errFmtDecodeHookCouldNotParse = "could not decode '%s' to a %s: %w"
|
||||
errFmtDecodeHookCouldNotParseEmptyValue = "could not decode an empty value to a %s: %w"
|
||||
errFmtDecodeHookCouldNotParse = "could not decode '%s' to a %s%s: %w"
|
||||
errFmtDecodeHookCouldNotParseBasic = "could not decode to a %s%s: %w"
|
||||
errFmtDecodeHookCouldNotParseEmptyValue = "could not decode an empty value to a %s%s: %w"
|
||||
)
|
||||
|
||||
var secretSuffixes = []string{"key", "secret", "password", "token"}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
|
@ -23,11 +25,11 @@ func StringToMailAddressHookFunc() mapstructure.DecodeHookFuncType {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
kindStr := "mail.Address (RFC5322)"
|
||||
prefixType := ""
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
ptr = true
|
||||
kindStr = "*" + kindStr
|
||||
prefixType = "*"
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(mail.Address{})
|
||||
|
@ -44,7 +46,7 @@ func StringToMailAddressHookFunc() mapstructure.DecodeHookFuncType {
|
|||
|
||||
if dataStr != "" {
|
||||
if result, err = mail.ParseAddress(dataStr); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType.String()+" (RFC5322)", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,11 +71,11 @@ func StringToURLHookFunc() mapstructure.DecodeHookFuncType {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
kindStr := "url.URL"
|
||||
prefixType := ""
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
ptr = true
|
||||
kindStr = "*" + kindStr
|
||||
prefixType = "*"
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(url.URL{})
|
||||
|
@ -90,7 +92,7 @@ func StringToURLHookFunc() mapstructure.DecodeHookFuncType {
|
|||
|
||||
if dataStr != "" {
|
||||
if result, err = url.Parse(dataStr); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,11 +121,11 @@ func ToTimeDurationHookFunc() mapstructure.DecodeHookFuncType {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
kindStr := "time.Duration"
|
||||
prefixType := ""
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
ptr = true
|
||||
kindStr = "*" + kindStr
|
||||
prefixType = "*"
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(time.Duration(0))
|
||||
|
@ -141,7 +143,7 @@ func ToTimeDurationHookFunc() mapstructure.DecodeHookFuncType {
|
|||
dataStr := data.(string)
|
||||
|
||||
if result, err = utils.ParseDurationString(dataStr); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||
}
|
||||
case f.Kind() == reflect.Int:
|
||||
seconds := data.(int)
|
||||
|
@ -176,11 +178,11 @@ func StringToRegexpHookFunc() mapstructure.DecodeHookFuncType {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
kindStr := "regexp.Regexp"
|
||||
prefixType := ""
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
ptr = true
|
||||
kindStr = "*" + kindStr
|
||||
prefixType = "*"
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(regexp.Regexp{})
|
||||
|
@ -197,7 +199,7 @@ func StringToRegexpHookFunc() mapstructure.DecodeHookFuncType {
|
|||
|
||||
if dataStr != "" {
|
||||
if result, err = regexp.Compile(dataStr); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,7 +208,7 @@ func StringToRegexpHookFunc() mapstructure.DecodeHookFuncType {
|
|||
}
|
||||
|
||||
if result == nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseEmptyValue, kindStr, errDecodeNonPtrMustHaveValue)
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseEmptyValue, prefixType, expectedType, errDecodeNonPtrMustHaveValue)
|
||||
}
|
||||
|
||||
return *result, nil
|
||||
|
@ -222,11 +224,11 @@ func StringToAddressHookFunc() mapstructure.DecodeHookFuncType {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
kindStr := "Address"
|
||||
prefixType := ""
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
ptr = true
|
||||
kindStr = "*" + kindStr
|
||||
prefixType = "*"
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(schema.Address{})
|
||||
|
@ -242,7 +244,7 @@ func StringToAddressHookFunc() mapstructure.DecodeHookFuncType {
|
|||
var result *schema.Address
|
||||
|
||||
if result, err = schema.NewAddressFromString(dataStr); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||
}
|
||||
|
||||
if ptr {
|
||||
|
@ -252,3 +254,131 @@ func StringToAddressHookFunc() mapstructure.DecodeHookFuncType {
|
|||
return *result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToX509CertificateHookFunc decodes strings to x509.Certificate's.
|
||||
func StringToX509CertificateHookFunc() mapstructure.DecodeHookFuncType {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(x509.Certificate{})
|
||||
|
||||
if t.Elem() != expectedType {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
dataStr := data.(string)
|
||||
|
||||
var result *x509.Certificate
|
||||
|
||||
if dataStr == "" {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var i interface{}
|
||||
|
||||
if i, err = utils.ParseX509FromPEM([]byte(dataStr)); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, err)
|
||||
}
|
||||
|
||||
switch r := i.(type) {
|
||||
case *x509.Certificate:
|
||||
return r, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StringToX509CertificateChainHookFunc decodes strings to schema.X509CertificateChain's.
|
||||
func StringToX509CertificateChainHookFunc() mapstructure.DecodeHookFuncType {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||
var ptr bool
|
||||
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
prefixType := ""
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
ptr = true
|
||||
prefixType = "*"
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(schema.X509CertificateChain{})
|
||||
|
||||
if ptr && t.Elem() != expectedType {
|
||||
return data, nil
|
||||
} else if !ptr && t != expectedType {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
dataStr := data.(string)
|
||||
|
||||
var result *schema.X509CertificateChain
|
||||
|
||||
if dataStr == "" && ptr {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if result, err = schema.NewX509CertificateChain(dataStr); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, prefixType, expectedType, err)
|
||||
}
|
||||
|
||||
if ptr {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
return schema.X509CertificateChain{}, nil
|
||||
}
|
||||
|
||||
return *result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToRSAPrivateKeyHookFunc decodes strings to rsa.PrivateKey's.
|
||||
func StringToRSAPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(rsa.PrivateKey{})
|
||||
|
||||
if t.Elem() != expectedType {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
dataStr := data.(string)
|
||||
|
||||
var result *rsa.PrivateKey
|
||||
|
||||
if dataStr == "" {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var i interface{}
|
||||
|
||||
if i, err = utils.ParseX509FromPEM([]byte(dataStr)); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, err)
|
||||
}
|
||||
|
||||
switch r := i.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return r, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package configuration_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
@ -833,7 +838,7 @@ func TestStringToAddressHookFunc(t *testing.T) {
|
|||
name: "ShouldFailDecode",
|
||||
have: "tcp://&!@^#*&!@#&*@!:2020",
|
||||
expected: schema.Address{},
|
||||
err: "could not decode 'tcp://&!@^#*&!@#&*@!:2020' to a Address: could not parse string 'tcp://&!@^#*&!@#&*@!:2020' as address: expected format is [<scheme>://]<ip>[:<port>]: parse \"tcp://&!@^\": invalid character \"^\" in host name",
|
||||
err: "could not decode 'tcp://&!@^#*&!@#&*@!:2020' to a schema.Address: could not parse string 'tcp://&!@^#*&!@#&*@!:2020' as address: expected format is [<scheme>://]<ip>[:<port>]: parse \"tcp://&!@^\": invalid character \"^\" in host name",
|
||||
decode: false,
|
||||
},
|
||||
}
|
||||
|
@ -862,6 +867,426 @@ func TestStringToAddressHookFunc(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStringToRSAPrivateKeyHookFunc(t *testing.T) {
|
||||
var nilkey *rsa.PrivateKey
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
{
|
||||
desc: "ShouldDecodeRSAPrivateKey",
|
||||
have: x509PrivateKeyRSA1,
|
||||
want: mustParseRSAPrivateKey(x509PrivateKeyRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeToECDSAPrivateKey",
|
||||
have: x509PrivateKeyRSA1,
|
||||
want: &ecdsa.PrivateKey{},
|
||||
decode: false,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeEmptyKey",
|
||||
have: "",
|
||||
want: nilkey,
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeECDSAKeyToRSAKey",
|
||||
have: x509PrivateKeyEC1,
|
||||
want: nilkey,
|
||||
decode: true,
|
||||
err: "could not decode to a *rsa.PrivateKey: the data is for a *ecdsa.PrivateKey not a *rsa.PrivateKey",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeBadRSAPrivateKey",
|
||||
have: x509PrivateKeyRSA2,
|
||||
want: nilkey,
|
||||
decode: true,
|
||||
err: "could not decode to a *rsa.PrivateKey: failed to parse PEM block containing the key",
|
||||
},
|
||||
}
|
||||
|
||||
hook := configuration.StringToRSAPrivateKeyHookFunc()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
||||
switch {
|
||||
case !tc.decode:
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.have, result)
|
||||
case tc.err == "":
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, tc.want, result)
|
||||
default:
|
||||
assert.EqualError(t, err, tc.err)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringToX509CertificateHookFunc(t *testing.T) {
|
||||
var nilkey *x509.Certificate
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificate",
|
||||
have: x509CertificateRSA1,
|
||||
want: mustParseX509Certificate(x509CertificateRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
want: mustParseX509Certificate(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACACertificate",
|
||||
have: x509CACertificateRSA,
|
||||
want: mustParseX509Certificate(x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
want: mustParseX509Certificate(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeEmptyCertificateToNil",
|
||||
have: "",
|
||||
want: nilkey,
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
||||
have: x509PrivateKeyEC1,
|
||||
want: nilkey,
|
||||
decode: true,
|
||||
err: "could not decode to a *x509.Certificate: the data is for a *ecdsa.PrivateKey not a *x509.Certificate",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeBadRSAPrivateKeyToCertificate",
|
||||
have: x509PrivateKeyRSA2,
|
||||
want: nilkey,
|
||||
decode: true,
|
||||
err: "could not decode to a *x509.Certificate: failed to parse PEM block containing the key",
|
||||
},
|
||||
}
|
||||
|
||||
hook := configuration.StringToX509CertificateHookFunc()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
||||
switch {
|
||||
case !tc.decode:
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.have, result)
|
||||
case tc.err == "":
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, tc.want, result)
|
||||
default:
|
||||
assert.EqualError(t, err, tc.err)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringToX509CertificateChainHookFunc(t *testing.T) {
|
||||
var nilkey *schema.X509CertificateChain
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
expected interface{}
|
||||
err, verr string
|
||||
decode bool
|
||||
}{
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificate",
|
||||
have: x509CertificateRSA1,
|
||||
expected: mustParseX509CertificateChain(x509CertificateRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificateNoPtr",
|
||||
have: x509CertificateRSA1,
|
||||
expected: *mustParseX509CertificateChain(x509CertificateRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificateChain",
|
||||
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificateChainNoPtr",
|
||||
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
expected: *mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeBadRSACertificateChain",
|
||||
have: buildChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||
verr: "certificate #1 in chain is not signed properly by certificate #2 in chain: x509: signature algorithm specifies an RSA public key, but have public key of type *ecdsa.PublicKey",
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACACertificate",
|
||||
have: x509CACertificateRSA,
|
||||
expected: mustParseX509CertificateChain(x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeEmptyCertificateToNil",
|
||||
have: "",
|
||||
expected: nilkey,
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeEmptyCertificateToEmptyStruct",
|
||||
have: "",
|
||||
expected: schema.X509CertificateChain{},
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
||||
have: x509PrivateKeyEC1,
|
||||
expected: nilkey,
|
||||
decode: true,
|
||||
err: "could not decode to a *schema.X509CertificateChain: the PEM data chain contains a EC PRIVATE KEY but only certificates are expected",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeBadRSAPrivateKeyToCertificate",
|
||||
have: x509PrivateKeyRSA2,
|
||||
expected: nilkey,
|
||||
decode: true,
|
||||
err: "could not decode to a *schema.X509CertificateChain: invalid PEM block",
|
||||
},
|
||||
}
|
||||
|
||||
hook := configuration.StringToX509CertificateChainHookFunc()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
actual, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.expected), tc.have)
|
||||
switch {
|
||||
case !tc.decode:
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.have, actual)
|
||||
case tc.err == "":
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, tc.expected, actual)
|
||||
|
||||
if tc.expected == nilkey {
|
||||
break
|
||||
}
|
||||
|
||||
switch chain := actual.(type) {
|
||||
case *schema.X509CertificateChain:
|
||||
require.NotNil(t, chain)
|
||||
if tc.verr == "" {
|
||||
assert.NoError(t, chain.Validate())
|
||||
} else {
|
||||
assert.EqualError(t, chain.Validate(), tc.verr)
|
||||
}
|
||||
case schema.X509CertificateChain:
|
||||
require.NotNil(t, chain)
|
||||
if tc.verr == "" {
|
||||
assert.NoError(t, chain.Validate())
|
||||
} else {
|
||||
assert.EqualError(t, chain.Validate(), tc.verr)
|
||||
}
|
||||
}
|
||||
default:
|
||||
assert.EqualError(t, err, tc.err)
|
||||
assert.Nil(t, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
x509PrivateKeyRSA1 = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA6z1LOg1ZCqb0lytXWZ+MRBpMHEXOoTOLYgfZXt1IYyE3Z758
|
||||
cyalk0NYQhY5cZDsXPYWPvAHiPMUxutWkoxFwby56S+AbIMa3/Is+ILrHRJs8Exn
|
||||
ZkpyrYFxPX12app2kErdmAkHSx0Z5/kuXiz96PHs8S8/ZbyZolLHzdfLtSzjvRm5
|
||||
Zue5iFzsf19NJz5CIBfv8g5lRwtE8wNJoRSpn1xq7fqfuA0weDNFPzjlNWRLy6aa
|
||||
rK7qJexRkmkCs4sLgyl+9NODYJpvmN8E1yhyC27E0joI6rBFVW7Ihv+cSPCdDzGp
|
||||
EWe81x3AeqAa3mjVqkiq4u4Z2i8JDgBaPboqJwIDAQABAoIBAAFdLZ58jVOefDSU
|
||||
L8F5R1rtvBs93GDa56f926jNJ6pLewLC+/2+757W+SAI+PRLntM7Kg3bXm/Q2QH+
|
||||
Q1Y+MflZmspbWCdI61L5GIGoYKyeers59i+FpvySj5GHtLQRiTZ0+Kv1AXHSDWBm
|
||||
9XneUOqU3IbZe0ifu1RRno72/VtjkGXbW8Mkkw+ohyGbIeTx/0/JQ6sSNZTT3Vk7
|
||||
8i4IXptq3HSF0/vqZuah8rShoeNq72pD1YLM9YPdL5by1QkDLnqATDiCpLBTCaNV
|
||||
I8sqYEun+HYbQzBj8ZACG2JVZpEEidONWQHw5BPWO95DSZYrVnEkuCqeH+u5vYt7
|
||||
CHuJ3AECgYEA+W3v5z+j91w1VPHS0VB3SCDMouycAMIUnJPAbt+0LPP0scUFsBGE
|
||||
hPAKddC54pmMZRQ2KIwBKiyWfCrJ8Xz8Yogn7fJgmwTHidJBr2WQpIEkNGlK3Dzi
|
||||
jXL2sh0yC7sHvn0DqiQ79l/e7yRbSnv2wrTJEczOOH2haD7/tBRyCYECgYEA8W+q
|
||||
E9YyGvEltnPFaOxofNZ8LHVcZSsQI5b6fc0iE7fjxFqeXPXEwGSOTwqQLQRiHn9b
|
||||
CfPmIG4Vhyq0otVmlPvUnfBZ2OK+tl5X2/mQFO3ROMdvpi0KYa994uqfJdSTaqLn
|
||||
jjoKFB906UFHnDQDLZUNiV1WwnkTglgLc+xrd6cCgYEAqqthyv6NyBTM3Tm2gcio
|
||||
Ra9Dtntl51LlXZnvwy3IkDXBCd6BHM9vuLKyxZiziGx+Vy90O1xI872cnot8sINQ
|
||||
Am+dur/tAEVN72zxyv0Y8qb2yfH96iKy9gxi5s75TnOEQgAygLnYWaWR2lorKRUX
|
||||
bHTdXBOiS58S0UzCFEslGIECgYBqkO4SKWYeTDhoKvuEj2yjRYyzlu28XeCWxOo1
|
||||
otiauX0YSyNBRt2cSgYiTzhKFng0m+QUJYp63/wymB/5C5Zmxi0XtWIDADpLhqLj
|
||||
HmmBQ2Mo26alQ5YkffBju0mZyhVzaQop1eZi8WuKFV1FThPlB7hc3E0SM5zv2Grd
|
||||
tQnOWwKBgQC40yZY0PcjuILhy+sIc0Wvh7LUA7taSdTye149kRvbvsCDN7Jh75lM
|
||||
USjhLXY0Nld2zBm9r8wMb81mXH29uvD+tDqqsICvyuKlA/tyzXR+QTr7dCVKVwu0
|
||||
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
x509PrivateKeyRSA2 = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
bad key
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
x509PrivateKeyEC1 = `
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIMn970LSn8aKVhBM4vyUmpZyEdCT4riN+Lp4QU04zUhYoAoGCCqGSM49
|
||||
AwEHoUQDQgAEMD69n22nd78GmaRDzy/s7muqhbc/OEnFS2mNtiRAA5FaX+kbkCB5
|
||||
8pu/k2jkaSVNZtBYKPVAibHkhvakjVb66A==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
x509CertificateRSA1 = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5TCCAc2gAwIBAgIQfBUmKLmEvMqS6S9auKCY2DANBgkqhkiG9w0BAQsFADAT
|
||||
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMjA5MDgxMDA5MThaFw0yMzA5MDgxMDA5
|
||||
MThaMBMxETAPBgNVBAoTCEF1dGhlbGlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEApqno1cOpDcgKmOJqeDQGIGH5/ZnqcJ4xud6eOUfbDqel3b0RkAQX
|
||||
mFYWEDO/PDOAOjYk/xSwZGo3jDofOHGhrKstQqLdweHGfme5NXYHJda7nGv/OY5q
|
||||
zUuEG4xBVgUsvbshWZ18H+bIQpwiP6tDAabxc0B7J15F1pArK8QN4pDTfsqZDwMi
|
||||
Qyo638XfUbDzEVZRbdDKxHz5g0w2vFdXon8uOxRRb0+zlHF9nM4PiESNgiUIYeua
|
||||
8Q5yP10SY2k9zlQ/OFJ4XhQmioCJvNjJE/TSc5/ECub2n7hTZhN5TGKagukZ5NAy
|
||||
KgbvNYW+CN+H4pFJt/9WptiDfBqhlUvjnwIDAQABozUwMzAOBgNVHQ8BAf8EBAMC
|
||||
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAH9veGzfWqXxsa5s2KHV2Jzed9V8KSs1Qy9QKRez1i2OMvMPh2DRM
|
||||
RLzAAp/XigjxLQF/LFXuoFW0Qg8BRb44iRgZrCiqVOUnd3xTrS/CcFExnpQI4F12
|
||||
/U70o97rkTonCOHmUUW6vQfWSXR/GU3/faRLJjiqcpWLZhTQNrnsip1ym3B2NMdk
|
||||
gMKkT8Acx1DX48MvTE4+DyqCS8TlJbacBJ2RFFELKu3jYnVNyrb0ywLxoCtWqBBE
|
||||
veVj+VMn9hNY1u5uydLsUDOlT5QyQcEuUzjjdhsJKEgDE5daNtB2OJJnd9IOMzUA
|
||||
hasPZETCCKabTpWiEPw1Cn/ZRqya0SZqFg==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
x509CACertificateRSA = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBDCCAeygAwIBAgIRAJfz0dHS9UkDngE55lUPdu4wDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNMjIwOTA4MDk1OTI1WhcNMjMwOTA4MDk1
|
||||
OTI1WjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBALfivbwq9r5X8N+NSbNHVuKbCb9f9vD5Xw2pOjSVvVjFkWQ1YKJu
|
||||
JGx9yskhHBZTBt76cInipA+0PqCBrBrjij1lh2StvzRVuQwgFG6H01LxBPi0JyYv
|
||||
Is94F6PHr6fSBgFWB5GNQ797KQIOdIr057uEFbp0eBMxxqiQ9gdyD0HPretrx1Uy
|
||||
kHuF6jck958combn9luHW0i53mt8706j7UAhxFqu9YUeklTM1VqUiRm5+nJKIdNA
|
||||
LiDMGVAuoxjhF6aIgY0yh5mL5mKtYYzhtA8WryrMzBgFRUGzHCSI1TNisA8wSf2T
|
||||
Z2JhbFHrFPR5fiSqAEHok3UXu++wsfl/lisCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
||||
AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
||||
OSXG42bCPNuWeP0ahScUMVjxe/wwDQYJKoZIhvcNAQELBQADggEBAFRnubHiYy1H
|
||||
PGODKA++UY9eAmmaCJxzuWpY8FY9fBz8/VBzcp8vaURPmmQ/34QcqfaSHDM2jIaL
|
||||
dQ2o9Ae5NjbRzLB6a5DcVO50oHG4BHP1ix4Bt3POr8J80KgA9pOIyAQqbAlFBSzQ
|
||||
l9yrzVULyf+qpUmByRf5qy2kQJOBfMJbn5j+BprWKwbcI8OAZWWSLItTXqJDrFTk
|
||||
OMZK4wZ6KiZM07KWMlwW/CE0QRzDk5MXfbwRt4D8pyx6rGKqI7QRusjm5osIpHZV
|
||||
26FdBdBvEhq4i8UsmDsQqH3iMY1AKmojZToZb5rStOZWHO/BZZ7nT2bscNjwm0E8
|
||||
6E2l6czk8ss=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
x509CACertificateECDSA = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBdzCCAR2gAwIBAgIQUzb62irYb/7B2H0c1AbriDAKBggqhkjOPQQDAjATMREw
|
||||
DwYDVQQKEwhBdXRoZWxpYTAeFw0yMjA5MDgxMDEzNDZaFw0yMzA5MDgxMDEzNDZa
|
||||
MBMxETAPBgNVBAoTCEF1dGhlbGlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||
b/EiIBpmifCI34JdI7luetygue2rTtoNH0QXhtrjMuZNugT29LUz+DobZQxvGsOY
|
||||
4TXzAQXq4gnTb7enNWFgsaNTMFEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdJQQIMAYG
|
||||
BFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUxlDPBKHKawuvhtQTN874
|
||||
TeCEKjkwCgYIKoZIzj0EAwIDSAAwRQIgAQeV01FZ/VkSERwaRKTeXAXxmKyc/05O
|
||||
uDv6M2spMi0CIQC8uOSMcv11vp1ylsGg38N6XYA+GQa1BHRd79+91hC+7w==
|
||||
-----END CERTIFICATE-----`
|
||||
)
|
||||
|
||||
func mustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||
block, _ := pem.Decode([]byte(data))
|
||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
||||
panic("not pem encoded")
|
||||
}
|
||||
|
||||
if block.Type != "RSA PRIVATE KEY" {
|
||||
panic("not private key")
|
||||
}
|
||||
|
||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func mustParseX509Certificate(data string) *x509.Certificate {
|
||||
block, _ := pem.Decode([]byte(data))
|
||||
if block == nil || len(block.Bytes) == 0 {
|
||||
panic("not a PEM")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return cert
|
||||
}
|
||||
|
||||
func buildChain(pems ...string) string {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
for i, data := range pems {
|
||||
if i != 0 {
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
buf.WriteString(data)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func mustParseX509CertificateChain(datas ...string) *schema.X509CertificateChain {
|
||||
chain, err := schema.NewX509CertificateChain(buildChain(datas...))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return chain
|
||||
}
|
||||
|
||||
func testInt32Ptr(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
|
|
@ -61,6 +61,9 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o inte
|
|||
StringToURLHookFunc(),
|
||||
StringToRegexpHookFunc(),
|
||||
StringToAddressHookFunc(),
|
||||
StringToX509CertificateHookFunc(),
|
||||
StringToX509CertificateChainHookFunc(),
|
||||
StringToRSAPrivateKeyHookFunc(),
|
||||
ToTimeDurationHookFunc(),
|
||||
),
|
||||
Metadata: nil,
|
||||
|
|
|
@ -48,7 +48,7 @@ func TestShouldErrorSecretNotExist(t *testing.T) {
|
|||
|
||||
errFmt := utils.GetExpectedErrTxt("filenotfound")
|
||||
|
||||
// ignore the errors before this as they are checked by the valdator.
|
||||
// ignore the errors before this as they are checked by the validator.
|
||||
assert.EqualError(t, errs[0], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "authentication"), "authentication_backend.ldap.password", fmt.Sprintf(errFmt, filepath.Join(dir, "authentication"))))
|
||||
assert.EqualError(t, errs[1], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "duo"), "duo_api.secret_key", fmt.Sprintf(errFmt, filepath.Join(dir, "duo"))))
|
||||
assert.EqualError(t, errs[2], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "jwt"), "jwt_secret", fmt.Sprintf(errFmt, filepath.Join(dir, "jwt"))))
|
||||
|
|
|
@ -57,3 +57,8 @@ const (
|
|||
// regexpHasScheme checks if a string has a scheme. Valid characters for schemes include alphanumeric, hyphen,
|
||||
// period, and plus characters.
|
||||
var regexpHasScheme = regexp.MustCompile(`^[-+.a-zA-Z\d]+://`)
|
||||
|
||||
const (
|
||||
blockCERTIFICATE = "CERTIFICATE"
|
||||
blockRSAPRIVATEKEY = "RSA PRIVATE KEY"
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
@ -13,7 +14,8 @@ type IdentityProvidersConfiguration struct {
|
|||
// OpenIDConnectConfiguration configuration for OpenID Connect.
|
||||
type OpenIDConnectConfiguration struct {
|
||||
HMACSecret string `koanf:"hmac_secret"`
|
||||
IssuerPrivateKey string `koanf:"issuer_private_key"`
|
||||
IssuerCertificateChain X509CertificateChain `koanf:"issuer_certificate_chain"`
|
||||
IssuerPrivateKey *rsa.PrivateKey `koanf:"issuer_private_key"`
|
||||
|
||||
AccessTokenLifespan time.Duration `koanf:"access_token_lifespan"`
|
||||
AuthorizeCodeLifespan time.Duration `koanf:"authorize_code_lifespan"`
|
||||
|
|
|
@ -18,6 +18,7 @@ var Keys = []string{
|
|||
"log.file_path",
|
||||
"log.keep_stdout",
|
||||
"identity_providers.oidc.hmac_secret",
|
||||
"identity_providers.oidc.issuer_certificate_chain",
|
||||
"identity_providers.oidc.issuer_private_key",
|
||||
"identity_providers.oidc.access_token_lifespan",
|
||||
"identity_providers.oidc.authorize_code_lifespan",
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewAddressFromString returns an *Address and error depending on the ability to parse the string as an Address.
|
||||
|
@ -99,3 +106,161 @@ func (a Address) HostPort() string {
|
|||
func (a Address) Listener() (net.Listener, error) {
|
||||
return net.Listen(a.Scheme, a.HostPort())
|
||||
}
|
||||
|
||||
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
||||
func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error) {
|
||||
if in == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chain = &X509CertificateChain{
|
||||
certs: []*x509.Certificate{},
|
||||
}
|
||||
|
||||
data := []byte(in)
|
||||
|
||||
var (
|
||||
block *pem.Block
|
||||
cert *x509.Certificate
|
||||
)
|
||||
|
||||
for {
|
||||
block, data = pem.Decode(data)
|
||||
|
||||
if block == nil || len(block.Bytes) == 0 {
|
||||
return nil, fmt.Errorf("invalid PEM block")
|
||||
}
|
||||
|
||||
if block.Type != blockCERTIFICATE {
|
||||
return nil, fmt.Errorf("the PEM data chain contains a %s but only certificates are expected", block.Type)
|
||||
}
|
||||
|
||||
if cert, err = x509.ParseCertificate(block.Bytes); err != nil {
|
||||
return nil, fmt.Errorf("the PEM data chain contains an invalid certificate: %w", err)
|
||||
}
|
||||
|
||||
chain.certs = append(chain.certs, cert)
|
||||
|
||||
if len(data) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
// X509CertificateChain is a helper struct that holds a list of *x509.Certificate's.
|
||||
type X509CertificateChain struct {
|
||||
certs []*x509.Certificate
|
||||
}
|
||||
|
||||
// Thumbprint returns the Thumbprint for the first certificate.
|
||||
func (c *X509CertificateChain) Thumbprint(hash crypto.Hash) []byte {
|
||||
if len(c.certs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
h := hash.New()
|
||||
|
||||
h.Write(c.certs[0].Raw)
|
||||
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// HasCertificates returns true if the chain has any certificates.
|
||||
func (c *X509CertificateChain) HasCertificates() (has bool) {
|
||||
return len(c.certs) != 0
|
||||
}
|
||||
|
||||
// Equal checks if the provided *x509.Certificate is equal to the first *x509.Certificate in the chain.
|
||||
func (c *X509CertificateChain) Equal(other *x509.Certificate) (equal bool) {
|
||||
if len(c.certs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return c.certs[0].Equal(other)
|
||||
}
|
||||
|
||||
// EqualKey checks if the provided key (public or private) has a public key equal to the first public key in this chain.
|
||||
//
|
||||
//nolint:gocyclo // This is an adequately clear function even with the complexity.
|
||||
func (c *X509CertificateChain) EqualKey(other any) (equal bool) {
|
||||
if len(c.certs) == 0 || other == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch key := other.(type) {
|
||||
case *rsa.PublicKey:
|
||||
return key.Equal(c.certs[0].PublicKey)
|
||||
case rsa.PublicKey:
|
||||
return key.Equal(c.certs[0].PublicKey)
|
||||
case *rsa.PrivateKey:
|
||||
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||
case rsa.PrivateKey:
|
||||
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||
case *ecdsa.PublicKey:
|
||||
return key.Equal(c.certs[0].PublicKey)
|
||||
case ecdsa.PublicKey:
|
||||
return key.Equal(c.certs[0].PublicKey)
|
||||
case *ecdsa.PrivateKey:
|
||||
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||
case ecdsa.PrivateKey:
|
||||
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||
case *ed25519.PublicKey:
|
||||
return key.Equal(c.certs[0].PublicKey)
|
||||
case ed25519.PublicKey:
|
||||
return key.Equal(c.certs[0].PublicKey)
|
||||
case *ed25519.PrivateKey:
|
||||
switch pub := key.Public().(type) {
|
||||
case *ed25519.PublicKey:
|
||||
return pub.Equal(c.certs[0].PublicKey)
|
||||
case ed25519.PublicKey:
|
||||
return pub.Equal(c.certs[0].PublicKey)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case ed25519.PrivateKey:
|
||||
switch pub := key.Public().(type) {
|
||||
case *ed25519.PublicKey:
|
||||
return pub.Equal(c.certs[0].PublicKey)
|
||||
case ed25519.PublicKey:
|
||||
return pub.Equal(c.certs[0].PublicKey)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Certificates for this X509CertificateChain.
|
||||
func (c *X509CertificateChain) Certificates() []*x509.Certificate {
|
||||
return c.certs
|
||||
}
|
||||
|
||||
// Validate the X509CertificateChain ensuring the certificates were provided in the correct order
|
||||
// (with nth being signed by the nth+1), and that all of the certificates are valid based on the current time.
|
||||
func (c *X509CertificateChain) Validate() (err error) {
|
||||
n := len(c.certs)
|
||||
now := time.Now()
|
||||
|
||||
for i, cert := range c.certs {
|
||||
if !cert.NotBefore.IsZero() && cert.NotBefore.After(now) {
|
||||
return fmt.Errorf("certificate #%d in chain is invalid before %d but the time is %d", i+1, cert.NotBefore.Unix(), now.Unix())
|
||||
}
|
||||
|
||||
if cert.NotAfter.Before(now) {
|
||||
return fmt.Errorf("certificate #%d in chain is invalid after %d but the time is %d", i+1, cert.NotAfter.Unix(), now.Unix())
|
||||
}
|
||||
|
||||
if i+1 >= n {
|
||||
break
|
||||
}
|
||||
|
||||
if err = cert.CheckSignatureFrom(c.certs[i+1]); err != nil {
|
||||
return fmt.Errorf("certificate #%d in chain is not signed properly by certificate #%d in chain: %w", i+1, i+2, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewAddressFromString(t *testing.T) {
|
||||
|
@ -48,3 +55,499 @@ func TestNewAddressFromString(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewX509CertificateChain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
have string
|
||||
thumbprintSHA256 string
|
||||
err string
|
||||
}{
|
||||
{"ShouldParseCertificate", x509CertificateRSA,
|
||||
"956e53c127b18143241fb75d2149369d41ad1e4094c40cedcef22e468b37886b", ""},
|
||||
{"ShouldParseCertificateChain", x509CertificateRSA + "\n" + x509CACertificateRSA,
|
||||
"956e53c127b18143241fb75d2149369d41ad1e4094c40cedcef22e468b37886b", ""},
|
||||
{"ShouldNotParseInvalidCertificate", x509CertificateRSAInvalid, "",
|
||||
"the PEM data chain contains an invalid certificate: x509: malformed certificate"},
|
||||
{"ShouldNotParseInvalidCertificateBlock", x509CertificateRSAInvalidBlock, "", "invalid PEM block"},
|
||||
{"ShouldNotParsePrivateKey", x509PrivateKeyRSA, "",
|
||||
"the PEM data chain contains a RSA PRIVATE KEY but only certificates are expected"},
|
||||
{"ShouldNotParseEmptyPEMBlock", x509CertificateEmpty, "", "invalid PEM block"},
|
||||
{"ShouldNotParseEmptyData", "", "", ""},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual, err := NewX509CertificateChain(tc.have)
|
||||
|
||||
switch len(tc.err) {
|
||||
case 0:
|
||||
switch len(tc.have) {
|
||||
case 0:
|
||||
assert.Nil(t, actual)
|
||||
default:
|
||||
assert.NotNil(t, actual)
|
||||
|
||||
assert.Equal(t, tc.thumbprintSHA256, fmt.Sprintf("%x", actual.Thumbprint(crypto.SHA256)))
|
||||
assert.True(t, actual.HasCertificates())
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
default:
|
||||
assert.Nil(t, actual)
|
||||
assert.EqualError(t, err, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestX509CertificateChain(t *testing.T) {
|
||||
chain := &X509CertificateChain{}
|
||||
|
||||
assert.Nil(t, chain.Thumbprint(crypto.SHA256))
|
||||
assert.False(t, chain.HasCertificates())
|
||||
assert.Len(t, chain.Certificates(), 0)
|
||||
|
||||
assert.False(t, chain.Equal(nil))
|
||||
assert.False(t, chain.Equal(&x509.Certificate{}))
|
||||
|
||||
assert.False(t, chain.EqualKey(nil))
|
||||
assert.False(t, chain.EqualKey(&rsa.PrivateKey{}))
|
||||
|
||||
cert := MustParseCertificate(x509CertificateRSA)
|
||||
cacert := MustParseCertificate(x509CACertificateRSA)
|
||||
|
||||
chain = MustParseX509CertificateChain(x509CertificateRSA + "\n" + x509CACertificateRSA)
|
||||
key := MustParseRSAPrivateKey(x509PrivateKeyRSA)
|
||||
|
||||
thumbprint := chain.Thumbprint(crypto.SHA256)
|
||||
assert.NotNil(t, thumbprint)
|
||||
assert.Equal(t, "956e53c127b18143241fb75d2149369d41ad1e4094c40cedcef22e468b37886b", fmt.Sprintf("%x", thumbprint))
|
||||
|
||||
assert.True(t, chain.Equal(cert))
|
||||
assert.False(t, chain.Equal(cacert))
|
||||
assert.True(t, chain.EqualKey(key))
|
||||
|
||||
assert.NoError(t, chain.Validate())
|
||||
|
||||
chain = MustParseX509CertificateChain(x509CertificateRSA + "\n" + x509CertificateRSA)
|
||||
assert.EqualError(t, chain.Validate(), "certificate #1 in chain is not signed properly by certificate #2 in chain: x509: invalid signature: parent certificate cannot sign this kind of certificate")
|
||||
|
||||
chain = MustParseX509CertificateChain(x509CertificateRSAExpired + "\n" + x509CACertificateRSAExpired)
|
||||
|
||||
err := chain.Validate()
|
||||
require.NotNil(t, err)
|
||||
assert.Regexp(t, regexp.MustCompile(`^certificate #1 in chain is invalid after 31536000 but the time is \d+$`), err.Error())
|
||||
|
||||
chain = MustParseX509CertificateChain(x509CertificateRSANotBefore + "\n" + x509CACertificateRSAotBefore)
|
||||
|
||||
err = chain.Validate()
|
||||
require.NotNil(t, err)
|
||||
assert.Regexp(t, regexp.MustCompile(`^certificate #1 in chain is invalid before 13569465600 but the time is \d+$`), err.Error())
|
||||
}
|
||||
|
||||
func MustParseX509CertificateChain(data string) *X509CertificateChain {
|
||||
chain, err := NewX509CertificateChain(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if chain == nil {
|
||||
panic("nil chain")
|
||||
}
|
||||
|
||||
return chain
|
||||
}
|
||||
|
||||
func MustParseCertificate(data string) *x509.Certificate {
|
||||
block, x := pem.Decode([]byte(data))
|
||||
if block == nil {
|
||||
panic("not pem")
|
||||
}
|
||||
|
||||
if len(x) != 0 {
|
||||
panic("extra data")
|
||||
}
|
||||
|
||||
if block.Type != blockCERTIFICATE {
|
||||
panic(fmt.Sprintf("not certifiate block: %s", block.Type))
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return cert
|
||||
}
|
||||
|
||||
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||
block, x := pem.Decode([]byte(data))
|
||||
if block == nil {
|
||||
panic("not pem")
|
||||
}
|
||||
|
||||
if len(x) != 0 {
|
||||
panic("extra data")
|
||||
}
|
||||
|
||||
if block.Type != blockRSAPRIVATEKEY {
|
||||
panic(fmt.Sprintf("not rsa private key block: %s", block.Type))
|
||||
}
|
||||
|
||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
var (
|
||||
// Valid from 2022 to 2122 (years).
|
||||
x509CertificateRSA = `-----BEGIN CERTIFICATE-----
|
||||
MIIC6DCCAdCgAwIBAgIRAIxvm0gFgsbh3D22rSZLuFQwDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwIBcNMjIxMDAyMDAzMDQyWhgPMjEyMjA5MDgw
|
||||
MDMwNDJaMBMxETAPBgNVBAoTCEF1dGhlbGlhMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAy71EOkV3jOpVQtVTH5HYcI4PryUCiAEyxAIuO+66gaAa4aCd
|
||||
UCRr8iO/pt5nOwPxjPo+hMHhkcKpX7evj+wgYXAccpIQFSCYWTJkaXFL0jL7yFuE
|
||||
5xpjgRM/x6FfK0IbN5WmVWO9EjesbyMCyDoYpjwzIrxnB70F9Y0nrXst1SnW/Sy0
|
||||
01BQZNzD1tky1KDvEkw7L5mMPZFZMr5wV+ELvbo1LLvvrGYhhzbXWk7pPbxT0gAa
|
||||
7yVvQbDKuCDqssAUyQa2JdlDaQocpldtK6l+dc3IsSWKd2UMouta75ngr9E1igy3
|
||||
t7owMRqH8NjwKHt6KQeDVSdBnWNjG572vaRimQIDAQABozUwMzAOBgNVHQ8BAf8E
|
||||
BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEAaZJ09yGa+DGr/iTqshGdPKNCtcf/CXCkL52xiI7DzLxDt30P
|
||||
8vCuXXrrTGBY7eWYupcNy/MyqaUrz1ED+map3nQzZQBJ9vWIfr01B9phkg/WSaNJ
|
||||
1DlYtbPYzr86BlGP1V5d3Wv6JqF3tkWHI0kI38CT68fWdDKrfa5j3JdZGIVJW+51
|
||||
U0IE3Nqhfc76YzwQ3sNX5FT2Fr55RowH+l5OBPk0Bcztq58XmyPR/bvPfDASt8iS
|
||||
DBT+0iiDiwk6LvOkasL8p7nuh5Grc9LMEYXY/QMUbkIWhIVRFlqyJA9s8vGHx1D4
|
||||
96iYKudj+yvO17Szzr/NNmcwETbCs4j6P6QeiA==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
// Private Key for x509CertificateRSA.
|
||||
x509PrivateKeyRSA = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAy71EOkV3jOpVQtVTH5HYcI4PryUCiAEyxAIuO+66gaAa4aCd
|
||||
UCRr8iO/pt5nOwPxjPo+hMHhkcKpX7evj+wgYXAccpIQFSCYWTJkaXFL0jL7yFuE
|
||||
5xpjgRM/x6FfK0IbN5WmVWO9EjesbyMCyDoYpjwzIrxnB70F9Y0nrXst1SnW/Sy0
|
||||
01BQZNzD1tky1KDvEkw7L5mMPZFZMr5wV+ELvbo1LLvvrGYhhzbXWk7pPbxT0gAa
|
||||
7yVvQbDKuCDqssAUyQa2JdlDaQocpldtK6l+dc3IsSWKd2UMouta75ngr9E1igy3
|
||||
t7owMRqH8NjwKHt6KQeDVSdBnWNjG572vaRimQIDAQABAoIBAA/EhhM8bRQqzo5t
|
||||
lBFNaELNu8kCRD/iV9tzj8BzqVt+2JW9qG8bYn9K5Po1HCglFfyjIVOE7cAqIJGX
|
||||
1a59x8PCuXDkfPolm6TLkZnXeta5u2K2MoLwN+M1aio5AvSGGTUkD8tr/KX8SQwQ
|
||||
2ZZFaML0xcBadF7U8jEey4NRlSp5/voiIAB+FrJHepZBz2XJYCX5s2vYLPMn+51R
|
||||
1HyO0n2aQ9H1Na8aBjTfAp9GDKJWBV3bSM7cVaLGlMFj/HNXUNVnSsVsJj0tdWKz
|
||||
K6r9zPskLnS+eNjCgqrOtZSqJ7M3PL0/PoTFPrr1Fevr+soKWCaPF94Ib94O9SEq
|
||||
scvP3kECgYEA0HBdGab0HjcZgFtsIaMm+eBcDhUmUrvMPUw6FmspKnc8wplscONW
|
||||
wrDGhR0dpT8+aAMD5jFC2pvyHjI5AWkW+53LB15j6SVzUlUMfS3VTwE2prLtDHDs
|
||||
nCDW2+fXY2kjv45efZGpMGbLJVePx2RCPzUlAlc14lzxnHgpo7eho1cCgYEA+jpi
|
||||
Eo/Jqa5CNd4hrXqFxZTFtU2Mn38ZKI3QK/l47/347yHLebjsYIIwJRoHenxWxNlz
|
||||
Y+BZ38vkP+f9BGAVGiRcyMmIJU0X305wKwl26Y2Q/tEm2OpwmDboD2pL9byi9vfY
|
||||
bz7pQGK/l9j86KofRwVJJRLsofPI1SsjnC8c448CgYAkpg0IjJ1RjriSJADwLSKW
|
||||
PseQxlE1rMVtZbC07mSPjeWGBbnWY3KGytQs5YCn5GXRne4alEC/9Tlt68CwKc0b
|
||||
spPXGNaSUL5lFIUcoWlm+bylNMKPNG+1x+RfR/VMCll5vcuJYooP85L2Xt3t3gfz
|
||||
2yFFtxXHVjY5H7uaiJgIAwKBgQDvkGXEj5TqtsL8/6YOiHb6Kuz+Hzi6mtxjTyI2
|
||||
d6mpWuWxTBGaf8kOvJWLb9gpFFGeNPGcdXaWJIZqCJjcT4Dkflu2f/uwepaYXGhX
|
||||
S8Bk6fwfee5PTmRt1mNmHsaKhgcfmznDh9+YnPIBVuULe5RmUlEtBWk3xEZKj/qP
|
||||
1Ss7UQKBgAwZQz+h5Z/XOJH3Qs5nJBKAZUiYkj3ux7G6tjx0cz7XcUYd/6enBpkY
|
||||
JeqVHB6G+bMRLwb+Hc5Vgpbd5GdaUWo8udaghHgSGPUVcn0lK38XhYek6ACGz7Lo
|
||||
xEfgtKoBlUq+uPb8H05HY0t9KybA3LA5wkRYYnJ17/nkZtrrJAmX
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
// Valid from 2022 to 2122 (years).
|
||||
x509CACertificateRSA = `-----BEGIN CERTIFICATE-----
|
||||
MIIDBTCCAe2gAwIBAgIQAK/NIAl3Bdg4Xk0y/ZGL7jANBgkqhkiG9w0BAQsFADAT
|
||||
MREwDwYDVQQKEwhBdXRoZWxpYTAgFw0yMjEwMDIwMDMwMjFaGA8yMTIyMDkwODAw
|
||||
MzAyMVowEzERMA8GA1UEChMIQXV0aGVsaWEwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQCg7jdO1HmydfkPzTtz57pvAS3YOdBT0hlNjJ4N2lrKhNnixrzK
|
||||
+4R1dWQDP2SHbZQ0TskF8eQ8HhTr7AsApotTthJFkUgV2g+bv7wVroz0Hok5xtd4
|
||||
bnpOvG3YUCP13Nk3ZVxdQXqR3/G3MrbyiXVPcgU+0giJ8EBykbtMu8L79/1iyk+m
|
||||
w4fZfzTOeorRgspO3z3+pTAib2MCTA7bby1dX9qI/ysFPLdbJYfNQDxij8SzNLyJ
|
||||
EkQ4kh3jKXf1VcZjbQTtYTZ3JJDqM08OxGMKuXUxPHd72Xlb+Fzql8LjYdEy/YKA
|
||||
3r8FMf14lzcjvxtLnFXh//hiXh4+xgXMkrLZAgMBAAGjUzBRMA4GA1UdDwEB/wQE
|
||||
AwICpDAPBgNVHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
|
||||
FGKpXiZA+8VQyMBqTTep+dVTthSbMA0GCSqGSIb3DQEBCwUAA4IBAQAE4DJg+Rb4
|
||||
iiocvxxQ85lhh94ql++E8MKuzIdN7ORs+ybUnsDD1WFDebubroTQuTSBkFrNuGNJ
|
||||
8B7NZsHiWWLvNsrnxxeC5CicqfhSDti0rKWsbGyeoq7Kqok5E4pwOzeRsxL2e/Hm
|
||||
G6LsUQuQMUG2vxKNynqmJS4VpgSVkiGhUfURFuRRDuRpVQ/XTl7jDIGf/ls7TAZq
|
||||
1AnmnSi4Cqy4hrTnwYUYkFCcH69onUKAoaVNl1eAH7ogxakz32WyWObY98NBrjzA
|
||||
I6VQlaQNSHtdFqDpu7NWJZZZSgN4BknbMYQEPNYCm701cPB4ahJbpg5C3pVPFSql
|
||||
Bc9iI6nN3PCr
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
/*
|
||||
// Private Key for x509CACertificateRSA.
|
||||
x509CAPrivateKeyRSA = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAoO43TtR5snX5D807c+e6bwEt2DnQU9IZTYyeDdpayoTZ4sa8
|
||||
yvuEdXVkAz9kh22UNE7JBfHkPB4U6+wLAKaLU7YSRZFIFdoPm7+8Fa6M9B6JOcbX
|
||||
eG56Trxt2FAj9dzZN2VcXUF6kd/xtzK28ol1T3IFPtIIifBAcpG7TLvC+/f9YspP
|
||||
psOH2X80znqK0YLKTt89/qUwIm9jAkwO228tXV/aiP8rBTy3WyWHzUA8Yo/EszS8
|
||||
iRJEOJId4yl39VXGY20E7WE2dySQ6jNPDsRjCrl1MTx3e9l5W/hc6pfC42HRMv2C
|
||||
gN6/BTH9eJc3I78bS5xV4f/4Yl4ePsYFzJKy2QIDAQABAoIBADlsZw3Y4Ufdsq6B
|
||||
w/oasLqVSB+EmaKfMGosh+VXidgDyZ+S3KDtWJl09uf1wdBVOHHlvvNBGfidn0eD
|
||||
pXVo+AQ5zpFGQtuRQMqJgvqVmzQshTi5i/8sJLZdpDBwgDRlxphusaORDsRojV6a
|
||||
WQ94HwTnIZoF5ggaU1TOTXAW+39er+3CAkHZlqeCHliSdVWdAPy2AGTOKqTP3Pko
|
||||
owbHkuCw53oWsDB9N8zqdVF+UBht0sFOQ8tEHq0OY1HJRtPhfvcFno9rADsna/Tg
|
||||
5m0sWUwP+uQ2+n18ahqunclzANu/w1SE+DVvXmeKdWMXEyuyL7muKsLgW5Ksa4jR
|
||||
h6gAwBUCgYEAwmigbqAWrVhh5W1t388WcfsD0M7a4vMg7a3L5Im5mThgUHu3K5bM
|
||||
EYLR8RnReEUdxdF86ASup4ddyVlz7z+YuQ9+XdTwMLK5Zujqfu9U/vsLHBc8h9eP
|
||||
B7C6etfnTBfQeQryPaako8mixBpS0/pQhDhBHpWcYLLTh+uE17+C2U8CgYEA0+pe
|
||||
EzWj7RHZiaHp6WWOqGuaCa3JR0uh2zns1gVx0cCdaCYFI+fOmrihrvBCYTXCiDZ4
|
||||
k4HGD48i1R8nUsV6HJfw0yhR2UmB46TnFYb0RgzuJhXlXtlivYVbSDhyzl1NBafC
|
||||
9zuCITMqGw921w/unOamMD94VSBfxT4EMBpvV1cCgYEAlhNuxfePigHQkOwJBd03
|
||||
1oWQTIFjOA+4O8MOwz4OqNl8gKUAogWnQ11Z9GWZ7t5sPWmaowH6UhmNrQIBHZBa
|
||||
tYHga08WnIFb3rWvUI4xbyUdTnIhqDwfjjA/xNUnGPbJWKe6mR0ru8TMgdZQWpPB
|
||||
1FAY9SNJtNxXr3WA94w/1sECgYBu0cEgioyPDSaVsvZ/93wC10JWjWsUvZiG7GPO
|
||||
CErdRb0LGdbWUALbJnJm6X3NGDACy3mCqfrJaDDvArutrVeOXGa0BgHHf4lNYo71
|
||||
0v0rJNflUs4AK+5W7cYunlZrVJ9StchfQd9rPTZnsE6VaN9/bZ663HYxDh0HKMdH
|
||||
4IsZQQKBgQCsBJ2bP9dc7QcqMXs8k37USIMs4nz6YWkV0Q74bBhbbnKIhY4mxhwf
|
||||
5WMBUntxYQT0yyygA8q/wJrjiWI9dr+Le0VAtg4Wn8CW0bNYvnMySn2++2E8m6jM
|
||||
DBXePomOtkIwEdLGcO8csBLYQKn4x/5ONpYI2QAifIB8Gdxqnp5clg==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
*/
|
||||
|
||||
// Valid from 1970 to 1971 (years).
|
||||
x509CertificateRSAExpired = `-----BEGIN CERTIFICATE-----
|
||||
MIIC5jCCAc6gAwIBAgIRAPKEPEnRO1hurtNAdEuDJA8wDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAN+qPAlnoqHMeBeXUF7qnaZXvHS4p8m2N9+hU8us6GYl3mYdFRDy
|
||||
PGBYWewtIE0RsexBa7UYOV6IXdfheipsmRZZzUxjPbP/VfNuafxdZMVgQzWZZAtt
|
||||
JJHRhLBATSfkutoPe3eUXxFonEhvl5ErU4327M9cZlLPRsIiVoTWOmigTT0jctx+
|
||||
u/3IyEVtV982SpttYnpCZ9lCvaSgjpvf1Mim+dbGF0KPKitAbuFnNpWsbRzIYfiy
|
||||
rGMvxuftkywJ/e6Lx34HJjq/4+K1qII9clIiwAxa1RTnLbBuSLzVHxmj3L5hQhap
|
||||
jf7HMhLReW2XLJNw4xUShSKpvapBRGbly18CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||
AQELBQADggEBAE5cRfJFTGOozfa9vOedNjif6MLqCtnVv9SRy7k/pdz0KSpYfpRo
|
||||
dPJ0kBIV2JwIVAXfge6SNn5DK/fyDzERq/kQaCzgBZ6//hCf6Y1BApGajLvOmhPx
|
||||
KijvMtqQ8IZnqwrURN04pQCt936grsas3Y6haxNyfdJDnQ5FgTe6kDOJU+rXyLp1
|
||||
N7H6hMXAd9+T++F50PM8AwtRZM6jSUVEhyrniKQSdkOQnXO5ng9if/7GntNzn56o
|
||||
7cV3sBeenxEmvaXsR30C+A+Ankxr8HBlVOCYcJpbtsCmOB2PVRq9Q5KJAylHRWE1
|
||||
JedOdWjWvrVaP2IqRopS9mV3Ckf1E19YWFg=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
/*
|
||||
// Private Key for x509CertificateRSAExpired.
|
||||
x509PrivateKeyRSAExpired = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA36o8CWeiocx4F5dQXuqdple8dLinybY336FTy6zoZiXeZh0V
|
||||
EPI8YFhZ7C0gTRGx7EFrtRg5Xohd1+F6KmyZFlnNTGM9s/9V825p/F1kxWBDNZlk
|
||||
C20kkdGEsEBNJ+S62g97d5RfEWicSG+XkStTjfbsz1xmUs9GwiJWhNY6aKBNPSNy
|
||||
3H67/cjIRW1X3zZKm21iekJn2UK9pKCOm9/UyKb51sYXQo8qK0Bu4Wc2laxtHMhh
|
||||
+LKsYy/G5+2TLAn97ovHfgcmOr/j4rWogj1yUiLADFrVFOctsG5IvNUfGaPcvmFC
|
||||
FqmN/scyEtF5bZcsk3DjFRKFIqm9qkFEZuXLXwIDAQABAoIBADBgYrHqD3wNfKAl
|
||||
o0WUW1riOSnJ0sjHN9iPzU8NbArEABF4Etlie3qfQXva2tSwkho2oDRANBBlUF7k
|
||||
LwdEC+yQqd3uzSbEgHOxmwzxql0ikAbk0YXDKpi7h4aTsdyCFYQauyrHFbTvOnZU
|
||||
ZKUKiPz4vomvQ5Z/rJ9KzAnZSDLeqbJfBXPPitlE8DAiYypGKDUmX0unMJh/x0Pw
|
||||
mIP/DTd+nMl+QpoSR0nS8r8Pr+4oBJ8K6k9Oni2DKdIW8IvoQJBBa9cm8Y0fHkSl
|
||||
hB7fncY5bE0lOZ8jBlSNuGfZHjVihwBA+rYAcWpyzdBx3SHRSe5AH4RKBPaERgSt
|
||||
SBV35PECgYEA7ayQs2SMggOiEK4Wf9AzywieaHiHa2ObJRg2dHlIgVkUDL3zB/b6
|
||||
57jPMXAtMyGQDW6pZF6Oq3mgYP2A9alB6QKjpX1OGFmqZJxtRMAm0KPs2C2inWzg
|
||||
dz9OW18jDlKKsHR00JktqsNgOZC8ldE2cyqgwBNXT/P9GyUMC9RmYhkCgYEA8Okk
|
||||
9u8IoIHJEWbtmmh0d1CEmPz4zQosTgUl2GLbNaCE/zDvA82YUQmi1yaF1FHjXMoa
|
||||
tD0Plkixoj/ezASeSE+duVpgXflYL4IHbqQq9JQg39vyaSuU1g3wP+hnmnCT62vb
|
||||
z4v7ugDLLkSlvNeEQLR3GvZvInZnfdwI5/mjeDcCgYA1UGlhJGP0YjY/gZ2gbCbC
|
||||
G5vVGXxfFYfeyVClzfL6uO2rcgyLM9bSlf08PMqW1qeGq9Upo6BjTLQyLYt5D8+u
|
||||
Ih5tZ+9VvP9g9En6ixPp52ugjpQUtjCf7z53dp7ZfqCHtofhpwq8bHkwUIxNGxIY
|
||||
wW4vx+blE3kqVqQeHzYcOQKBgBgBAv/fvVpQ1Dn5qX8THVeuHCgqPJghhVyYwraW
|
||||
0wS648WRmJ8mYyDf9uu9GOSY7DCYqqR+2Qi+YYSrHIXzh9nopOyNBsEWUSUarabm
|
||||
kKkiAUyM29CC2Sei5+dWPsxynyp76sD5T7Gu1o/boy/3wWO5F40GNPiYF6PAwtpq
|
||||
U1FtAoGBANfr5OcnCIdtHLCEVRCaJdzTkQj5X1g9dF0D/gWkBIF0hibcs9yV2i1Z
|
||||
JtxBrOvctkRsY7/C8dCms1gkfwDyTpKuMk9iDd3wfDGP3LdD1+V10pCm5ShHIGNm
|
||||
/pRFpN45nR5iCX9mnvr8YJLUsrBkh7N4c4ao8xsXzOLBOk8WvtXL
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
*/
|
||||
|
||||
// Valid from 1970 to 1971 (years).
|
||||
x509CACertificateRSAExpired = `-----BEGIN CERTIFICATE-----
|
||||
MIIDAzCCAeugAwIBAgIQB07G1WhPAiAHaM6FogkZ7TANBgkqhkiG9w0BAQsFADAT
|
||||
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw03MDAxMDEwMDAwMDBaFw03MTAxMDEwMDAw
|
||||
MDBaMBMxETAPBgNVBAoTCEF1dGhlbGlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEArly1jZXqLaAQJwTaoT0QaePV7SejQZj418Id7X7Gx1bgjmIS0mL8
|
||||
058c8Av1ThRaUmVAMkbBc4MED9KnKKckU+qMBV0+2xovrn7TvAKHG3yLUtasn/Uz
|
||||
2UzBXMgbCIMVCloWkPQ6BuSaNsZnSBmTj/IC16bAZwm5lIS9ZjzQ+QnZg8x5ftNR
|
||||
Jx8Gar8xb4tQbhb6uZU5zxfuV1+4qk04lf6E48IVrf57NBXpJhdzxuvdBj0l46k3
|
||||
zf44gybrXpr9O0n0Eb/H8lkIoIDM+vanoRvdg868QQw8C/r06M4E8gJJzZk7Ad2c
|
||||
oCq66peom6eLUSo2DVfVmmQ2KjLUyW0L6QIDAQABo1MwUTAOBgNVHQ8BAf8EBAMC
|
||||
AqQwDwYDVR0lBAgwBgYEVR0lADAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSQ
|
||||
qzXTP6jV517cAknynvP0vr4ChzANBgkqhkiG9w0BAQsFAAOCAQEAGvX14cJM+uxh
|
||||
rSBYa3qHEfSvlSwbQsCxZv5VXLJj6SLYUXqMLxCuPH16ZKD8MygmnF5/8Mp3ZIdF
|
||||
Aesehtaoth1H06q6iXWw+wgT9fZKGIHL4RftvjFWncD+Lbk8hP/DkLqsGt+Mj23u
|
||||
JAByhiG8QmbXu/X7kfXSvXjhQ7f7C+bNKxb03r7mT5gI8mCUp5MyLp3DPk3dKTwa
|
||||
uby/wjlFMHi92HjfQ6mCn5ijc/ltiMh1wtXf53IEESYvrWV5ABjV8xnumI4j4idB
|
||||
7yHjCn5id379go8e8a+W8jODNzUORzSio8tDhL4c13tiD4PzlMJ1tUr+CIoCzqB5
|
||||
m99SvwJ74A==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
/*
|
||||
// Private Key for x509CACertificateRSAExpired.
|
||||
x509CAPrivateKeyRSAExpired = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEArly1jZXqLaAQJwTaoT0QaePV7SejQZj418Id7X7Gx1bgjmIS
|
||||
0mL8058c8Av1ThRaUmVAMkbBc4MED9KnKKckU+qMBV0+2xovrn7TvAKHG3yLUtas
|
||||
n/Uz2UzBXMgbCIMVCloWkPQ6BuSaNsZnSBmTj/IC16bAZwm5lIS9ZjzQ+QnZg8x5
|
||||
ftNRJx8Gar8xb4tQbhb6uZU5zxfuV1+4qk04lf6E48IVrf57NBXpJhdzxuvdBj0l
|
||||
46k3zf44gybrXpr9O0n0Eb/H8lkIoIDM+vanoRvdg868QQw8C/r06M4E8gJJzZk7
|
||||
Ad2coCq66peom6eLUSo2DVfVmmQ2KjLUyW0L6QIDAQABAoIBAAiOZBpekO9MO36u
|
||||
rkvbQ0Lu+0B4AXrmls9/pxhQcFC34q0aAvJwCRgZZsIg1BjQxt3kOhI9hqC0fS6J
|
||||
l8pW6WF00QoyWTNHRa+6aYmAVkDzC6M1BaOT1MeFDLgQ2cLBK/cmFJVoZrCP50Fo
|
||||
2wieuK8HoTwT4r0rrP+sw96QfXC7BjC1VSL9GXYemKz0RXEUvXXmzGGc9YE8vCt/
|
||||
PXOb4TV30TIQrivkywSTJi8A1jUjYI2rPgo6JCl6GZGmc7hVX4jJ9lbBhUH76ozO
|
||||
KS1Yzo/veWL4rVspc2exT5cuX7JIuFCjVi0Nlv1MKv39jpfTfKQh0ug6pHlxUzqX
|
||||
Rl6Ln0ECgYEAwX0HtsmKMoSIrU4k90TKyhzjNoL8EaMKrSoZ4nK599igd8g+y6eD
|
||||
jc1qO60lOHObyLPFor0rQT7K2KCD7GKYSX5+sh9leQEASl7cJkSAG17WBlrf9Pas
|
||||
nUXjTRx3moEILAWmuov4UrYpkuEFk65d98xP3uPtDylFj57Bc+a8DOcCgYEA5rHK
|
||||
qdjE8c0/1kgmItDJjmKxrJ5K5hY4w7SkpZeg4Rwr2WAjv3je/nx34D8S7m6R2uzp
|
||||
NQYAAHXdzHt4iegupyW/3UXJboEscSTuC6/v3llawAozh02nDsMrdC1LreQ1IiFy
|
||||
mKDmPZWxiAZXxEJ0hi0YMCcQnBY673eAleostq8CgYEAtia/mVvYh0Bv7z9O253e
|
||||
jzFs0ce0B+KGzYiB/8XjvyknwDw6qbzUwy0romyZSrDDasma+F7AFtdHXXKXX3Ve
|
||||
SmoUWhnmjGjd3iW5eSkptRqtwCPTDKkgzZqapuBy1Hg+ujrDwIC+0Rb+wnCmsGYJ
|
||||
vpuQYZQPeyNugguBsVv5kucCgYBToTRE6k5LEgsIVVNt356RvXmHiELCsl+VotDl
|
||||
Ltilgp7qyI1tBhZgzyJt6q+kO/UoFiZckHZDtHbZgBEsfT0cXvT09C2Xn8BKrAaX
|
||||
ugoM4vuhDpGrhR0AnwQLs7fxq/8PBm0So5GT1cZr91Ct1yGC2qogGqlMzEpFMV8t
|
||||
+ZyIBQKBgFyI2cZ3/uHQMkWUguml9w134bIGpqGdp8jf9YTvWs3Ax8/qxAVmFmtm
|
||||
fot+QiamambblrhdT6pau11Mp06FzytorQH0qKd8mPAqvqtvcSoDZzqXjrkUnZIx
|
||||
uLUcfb41clRhlfGDUj5MWimfi7d/Vakh86zAa8pg5WBCtXr0bZ/j
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
*/
|
||||
|
||||
// Valid from 2400 to 2401 (years).
|
||||
x509CertificateRSANotBefore = `-----BEGIN CERTIFICATE-----
|
||||
MIIC6TCCAdGgAwIBAgIQYQWHYM90zNnwv22xhOPkszANBgkqhkiG9w0BAQsFADAT
|
||||
MREwDwYDVQQKEwhBdXRoZWxpYTAiGA8yNDAwMDEwMTAwMDAwMFoYDzI0MDAxMjMx
|
||||
MDAwMDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAPFYYindnRfs8aJfXbaX5IRVj10uKlT4i0BwJ5IYaC4O/3UQ
|
||||
km7do8lL2Ea2N2L5tQJhk2d+yoWGPeaUyuYP692jPA+4BW6RPuroSPB9WEU+x1ir
|
||||
it/AzJtavg0Lu2fGZkXxAZJj2MlrXT7csaGwRAvvPEHS6EJW4UtERYIqfpKGB39I
|
||||
sUhKNvY3edF9sosUAJmiZ8Q4K/uYoyCxyiE1QKLaiIjcZJxtzXkzwVBy1ZlmG+r5
|
||||
VNNguQQFsS8f7uRlOmo0o3hDG9dByUn7PgFEExbgBtdmNoIPk/pfMFM8NIHK+wOC
|
||||
q/SO2e/MX0IhJZXfq2VTZFgrisPovg8GpHSHRCkCAwEAAaM1MDMwDgYDVR0PAQH/
|
||||
BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZI
|
||||
hvcNAQELBQADggEBADBTRbfg/UQeJpdMogm9tleXJBcHqgOgiBxkKYxGSlRg4vlr
|
||||
tM8USAr24whLvb8KDhT6PaSY8wyPuCxqwqiKR84eselxOAcgDLV9n36OcWRm+oFl
|
||||
Th1S1JUtjbrctU7i6pp4BwUmBwkVALrbrj+cGVG7uBfbP8L/onmjg9KkY5ttnbyb
|
||||
Qi/Pv9zYLEo394hL3oeaphkP0iE6cHOvII/qFhnpLREINGp3g8V2I46Id/xYDi22
|
||||
WRabgMuFGpel7Q26yh+YXyHoIkKdiOXMNNXTsuvp5EDBGybTIXWK3xrqTsREMVDr
|
||||
EmiaOgL8c7+PpSWuUggJLb/JXDYnPtvekH3gPao=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
/*
|
||||
// Private Key for x509CertificateRSANotBefore.
|
||||
x509PrivateKeyRSANotBefore = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA8VhiKd2dF+zxol9dtpfkhFWPXS4qVPiLQHAnkhhoLg7/dRCS
|
||||
bt2jyUvYRrY3Yvm1AmGTZ37KhYY95pTK5g/r3aM8D7gFbpE+6uhI8H1YRT7HWKuK
|
||||
38DMm1q+DQu7Z8ZmRfEBkmPYyWtdPtyxobBEC+88QdLoQlbhS0RFgip+koYHf0ix
|
||||
SEo29jd50X2yixQAmaJnxDgr+5ijILHKITVAotqIiNxknG3NeTPBUHLVmWYb6vlU
|
||||
02C5BAWxLx/u5GU6ajSjeEMb10HJSfs+AUQTFuAG12Y2gg+T+l8wUzw0gcr7A4Kr
|
||||
9I7Z78xfQiElld+rZVNkWCuKw+i+DwakdIdEKQIDAQABAoIBACGcZHdeJK2bUv+A
|
||||
9oUiXDHN1JxufHi+8G218NzYx1F6xzrfZvVHqrKy/FjEsav4CKxfOG8Wak/0JRTC
|
||||
rgsiNn/0Zr3tq9v9IF0IonfTjQJ/vrVrlniY2iXcmlEozB2ktMOSz9w6SYurhx3l
|
||||
EFvrN17OH38vRydOACxCQsfg8SWofY6SV0gcvlCcuM4lKBiuOBWGcf+xwIs3B+Bs
|
||||
Frd282jRWtlcYd+zDE+vLxugNizLGpRKCMEdcKPRw9fkBKDI/f56WegNTUZYYFrV
|
||||
LEmYIbOwMawvbi0mOdLsp27CfmeUjkEbwzgdNwjFrWIFAk0wT3QvDrKxDYDLM2Z3
|
||||
+PtBMwECgYEA9ICYgzPMbN3CsQ+eWSQXXNk35V7PlMl1DC4UIHhi53BMT1ZhvkHf
|
||||
D+eqXQ3BSqOUR7b417VBGkK8UtQuQXh9FwwVU0RhVkjpX0nTBhe8gGF3f3094rX4
|
||||
Ckhm8XYQEWCUA9HNhCW+KSNVWqgw9Qi0awEY7HaiR628br39/EckvokCgYEA/LHI
|
||||
HA9ixEBeTjds52rK6n9bPHeI87qxF62lLQYXvosyJij9/ruUfXwfJjG3EvCfcW7N
|
||||
fr2EvgzPbCozC1V6gI9k5CXhOsf+wD6M8A7g7YHUa/dPq2B8bfqaMD0vW7OoZiLQ
|
||||
NpfMtBvZxd1wukPGypLGWabPLo8u6bSfxTqz8KECgYAudnmFBUTls0aaKyOmQOuH
|
||||
o2ex2NCNr7Lke6UrfnUdEgQOV5X/d7kR5q5DPKfsrSUyc5zaMQGMIf5zpwqbOnBa
|
||||
/trWlfoBUZ23k+ncEIqrwtnYik5GVNor6hJV9F+dTcMS7r2lTR7T5nkD305eYicW
|
||||
5oB7/xdbk7JpQQWQ+VwMMQKBgQC3hbKs1mvH1mvnaI+aftACgR5VCweW4/bsGHwG
|
||||
+A7Unyl713eowrk0ban9xkuM4N8btfpe2uuGT61xhDBwQdNnfT0sCWrLkyasnoEj
|
||||
c9reA9Wv1/yvnbKg+Ul0UWuMsS1TiGMp0xOjlzqRXqMZVFITG4gc4m5EBU9wAnOq
|
||||
/VhkIQKBgBUwpoL+K1OswQKV/SHH7n5b/By02mLuMkQR0NlpWRJY6eTDk3FAUkHn
|
||||
+T996U0a+7OY+mATVqrfLBcsa6i+HGpb4jZL+kkdtfmtHUnN8YOAwopWp3uoPRmF
|
||||
lpakAQq8NPVcrX6PLDkHeOlKhE4ercYFqcCRKcnMB3nD+re6x5l6
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
*/
|
||||
|
||||
// Valid from 2400 to 2401 (years).
|
||||
x509CACertificateRSAotBefore = `-----BEGIN CERTIFICATE-----
|
||||
MIIDBzCCAe+gAwIBAgIQeR2/TbyH9gEzyjuTijMGVzANBgkqhkiG9w0BAQsFADAT
|
||||
MREwDwYDVQQKEwhBdXRoZWxpYTAiGA8yNDAwMDEwMTAwMDAwMFoYDzI0MDAxMjMx
|
||||
MDAwMDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAJ7vpRSDXVvwOLGmjbZdoG25OdsWgmhVAWpFCUifotqorz+z
|
||||
+VgwPnvVDp1cTp07y+mDJK++GNBOG1pS5G4or6Y1HAlT3nGpE0FYhrtDBQmhvqV0
|
||||
6mPM/Dq5JIuGiju4LX0KBlaFJugJevw3ySnoPUu0BQ9mTZUgggNwetqsAX7TioFj
|
||||
TkVIMtgranigOvWjJQyLlmiK1TOgMqgWYNR20SE4CkmIp9SjOdeW7kNVMOojRx9a
|
||||
VgElA2TN57/Je+/tLbvTDDCP9o59SIXxn5N6JQ2/XdDZPNBHrxnmchQVDXWCkP0A
|
||||
gkV7V1dl8ur85iEdN+F31Kvd0nzCCaC3YUMxgmcCAwEAAaNTMFEwDgYDVR0PAQH/
|
||||
BAQDAgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQUB4/oEXJWhMwKBpMesSOnQdD+G0UwDQYJKoZIhvcNAQELBQADggEBAIlzOnWB
|
||||
xIhMm3zpLfpJGBi62d9J7Rlf5NitWoztyHdJpQ9y99s67QonR7UY7n11PMdtsLja
|
||||
hWy1/iZ1o5F2zGKPzSS8pscdIuo4B+TocLiHkEx7ttyQ0MepoDt1RlTOjqilqbfD
|
||||
A4GyGidns1VZuH8wP8NpZNlWajsXgvkYT433RzPgKe7qoI3DFQwc72SBuZSHHyjE
|
||||
9SVgdN0KmfFXMum4BurwftelF1etGR+4II3cDG80CH2ZvYdqCURPoa+ny/qqMtzq
|
||||
W2CnwP59TrotQgKCFJS5EdL3MXaZSvK9z2LERdxDvp4OSoJYoxSMJawfkVwZ15rk
|
||||
apA21VwIrpFg54A=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
/*
|
||||
// Private Key for x509CACertificateRSAotBefore.
|
||||
x509CAPrivateKeyRSANotBefore = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAnu+lFINdW/A4saaNtl2gbbk52xaCaFUBakUJSJ+i2qivP7P5
|
||||
WDA+e9UOnVxOnTvL6YMkr74Y0E4bWlLkbiivpjUcCVPecakTQViGu0MFCaG+pXTq
|
||||
Y8z8Orkki4aKO7gtfQoGVoUm6Al6/DfJKeg9S7QFD2ZNlSCCA3B62qwBftOKgWNO
|
||||
RUgy2CtqeKA69aMlDIuWaIrVM6AyqBZg1HbRITgKSYin1KM515buQ1Uw6iNHH1pW
|
||||
ASUDZM3nv8l77+0tu9MMMI/2jn1IhfGfk3olDb9d0Nk80EevGeZyFBUNdYKQ/QCC
|
||||
RXtXV2Xy6vzmIR034XfUq93SfMIJoLdhQzGCZwIDAQABAoIBAEneATBOeYZwWDkg
|
||||
un5Gd3hnfN85T/SjhVvZqB3rq6nKemC2Ca4WBgRRmlBChXsIPpZR0CwpwqiVlJrf
|
||||
KbGVEUXDKzuekiTrOrrFJSFFXcMDPHLzqrglnhjA0Z5TMk3dJK8XiKiPi+yN823j
|
||||
k4f5mvtjOHLWzjn/+M0WatLU3IEPnqpnE+pEKrkZQa7Mg+xHprvt67Q4aCgP5lfy
|
||||
A04eoUo7+TMRsK718vb02E81ZQSLgSbQMd0W8Dkt7vRkYRNL0OKBlPQcP9qZlw5s
|
||||
swy4ne9jgmJKY3mmdnURTjvdJb20dUsSSzseZ8Tj6UYUDntXrB62YhZvC0ZRhGY/
|
||||
Nnf10IECgYEAzSi1l1G6ZblV2g2jPqqD4EsdUvitnN5t592dw52+SyizNj7j+pLt
|
||||
OPi+bt5HW4orHQHlPi6wt1BQIZ7UVHmljKQq7ByxOX0SRrRg428JPlFMjCp3bG1t
|
||||
zmRQwADGkfqn3JQcmY9VDjtn5oCx18bNpDR1gNiK6zImK/jmWBOiaKcCgYEAxlKN
|
||||
vYeG70ZrtVBz6jmX6yOtN8/hBA0mifZVHmkKX6earU3Ds2Uj2Hg+AgMqoFD2aRSp
|
||||
wodEYzV6hSpvdLzqBi6SklnfF4NBqJ51TEFBWaVMUZjTwConOcc+vvvSDbCkmnoF
|
||||
yTcqVm2p91HD7859ACcO+m8nsFJGldbJFl8RkEECgYEAkSLajEkyH2Kk3JTHRr7k
|
||||
eplJDniEgbRNdjmusUN36r3JQnftWkf08FfwiIhRXO37IBNGNN5c/+IePhqZxYUl
|
||||
W8CL6OtHaQ8VDdXvsRXNKTvkdkhYoeksRFVtVtd1orH7bK2PKgdfOalHEKc8qRSo
|
||||
SCEge101sbuRi4wSkH6bZ4MCgYAerDXv0j40U5fk+wRyfWXZoDLyJtyOW9pSDB8u
|
||||
DODl2m45z4UtAb+Bg1dTyFmXYe46Yk+/HlydW3APmHiUfYNUYW+Z4vx2Dn7hLWDG
|
||||
4nDRBJfBJvnZBqv6a65wq1HZfDB5E9ZBQJ7zrxJShfrf4/fBRkkywm5I/vCbzBRd
|
||||
uWZmAQKBgQCJE5rx3rWZz+srunmmkg5LXBMveD+1HRvlwlj/gELG2b2ytNUmnVey
|
||||
2naApHnvW7lZdrADpbzLKGEDB/EsaIPJjqQw45OoIZwPdM4bm0/w5c1ZLnStXGCz
|
||||
Th/7Sva6x6FW7tHY6ldqybcMj8w3kA4ByQEOg2BtPnWTm1NX/qcr8Q==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
*/
|
||||
|
||||
x509CertificateRSAInvalid = `-----BEGIN CERTIFICATE-----
|
||||
mIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urpfO4E7owDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIwFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
||||
/Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
||||
LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
||||
91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
||||
kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39n0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
||||
Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||
AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
||||
/ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
||||
lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
||||
wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
||||
OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
||||
ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
x509CertificateRSAInvalidBlock = `-----BEGIN CERTIFICATE-----
|
||||
MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
||||
/Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
||||
LuYx2rBYSlMSN5UZQm/RxMtXf^K2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
||||
91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
||||
kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
||||
Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||
AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
||||
/ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
||||
lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
||||
wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
||||
OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
||||
ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
x509CertificateEmpty = `-----BEGIN CERTIFICATE-----
|
||||
-----END CERTIFICATE-----`
|
||||
)
|
||||
|
|
|
@ -132,6 +132,8 @@ const (
|
|||
errFmtOIDCNoClientsConfigured = "identity_providers: oidc: option 'clients' must have one or " +
|
||||
"more clients configured"
|
||||
errFmtOIDCNoPrivateKey = "identity_providers: oidc: option 'issuer_private_key' is required"
|
||||
errFmtOIDCCertificateMismatch = "identity_providers: oidc: option 'issuer_private_key' does not appear to be the private key the certificate provided by option 'issuer_certificate_chain'"
|
||||
errFmtOIDCCertificateChain = "identity_providers: oidc: option 'issuer_certificate_chain' produced an error during validation of the chain: %w"
|
||||
errFmtOIDCEnforcePKCEInvalidValue = "identity_providers: oidc: option 'enforce_pkce' must be 'never', " +
|
||||
"'public_clients_only' or 'always', but it is configured as '%s'"
|
||||
|
||||
|
@ -294,9 +296,11 @@ var validSessionSameSiteValues = []string{"none", "lax", "strict"}
|
|||
var validLoLevels = []string{"trace", "debug", "info", "warn", "error"}
|
||||
|
||||
var validWebauthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)}
|
||||
|
||||
var validWebauthnUserVerificationRequirement = []string{string(protocol.VerificationDiscouraged), string(protocol.VerificationPreferred), string(protocol.VerificationRequired)}
|
||||
|
||||
var validRFC7231HTTPMethodVerbs = []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", "CONNECT", "OPTIONS"}
|
||||
|
||||
var validRFC4918HTTPMethodVerbs = []string{"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK"}
|
||||
|
||||
var validACLHTTPMethodVerbs = append(validRFC7231HTTPMethodVerbs, validRFC4918HTTPMethodVerbs...)
|
||||
|
@ -306,9 +310,13 @@ var validACLRulePolicies = []string{policyBypass, policyOneFactor, policyTwoFact
|
|||
var validDefault2FAMethods = []string{"totp", "webauthn", "mobile_push"}
|
||||
|
||||
var validOIDCScopes = []string{oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeProfile, oidc.ScopeGroups, oidc.ScopeOfflineAccess}
|
||||
|
||||
var validOIDCGrantTypes = []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"}
|
||||
|
||||
var validOIDCResponseModes = []string{"form_post", "query", "fragment"}
|
||||
|
||||
var validOIDCUserinfoAlgorithms = []string{"none", "RS256"}
|
||||
|
||||
var validOIDCCORSEndpoints = []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint}
|
||||
|
||||
var reKeyReplacer = regexp.MustCompile(`\[\d+]`)
|
||||
|
|
|
@ -16,11 +16,42 @@ func ValidateIdentityProviders(config *schema.IdentityProvidersConfiguration, va
|
|||
}
|
||||
|
||||
func validateOIDC(config *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
|
||||
if config != nil {
|
||||
if config.IssuerPrivateKey == "" {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCNoPrivateKey))
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
|
||||
setOIDCDefaults(config)
|
||||
|
||||
if config.IssuerPrivateKey == nil {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCNoPrivateKey))
|
||||
} else if config.IssuerCertificateChain.HasCertificates() {
|
||||
if !config.IssuerCertificateChain.EqualKey(config.IssuerPrivateKey) {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCCertificateMismatch))
|
||||
}
|
||||
|
||||
if err := config.IssuerCertificateChain.Validate(); err != nil {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCCertificateChain, err))
|
||||
}
|
||||
}
|
||||
|
||||
if config.MinimumParameterEntropy != 0 && config.MinimumParameterEntropy < 8 {
|
||||
validator.PushWarning(fmt.Errorf(errFmtOIDCServerInsecureParameterEntropy, config.MinimumParameterEntropy))
|
||||
}
|
||||
|
||||
if config.EnforcePKCE != "never" && config.EnforcePKCE != "public_clients_only" && config.EnforcePKCE != "always" {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCEnforcePKCEInvalidValue, config.EnforcePKCE))
|
||||
}
|
||||
|
||||
validateOIDCOptionsCORS(config, validator)
|
||||
|
||||
if len(config.Clients) == 0 {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCNoClientsConfigured))
|
||||
} else {
|
||||
validateOIDCClients(config, validator)
|
||||
}
|
||||
}
|
||||
|
||||
func setOIDCDefaults(config *schema.OpenIDConnectConfiguration) {
|
||||
if config.AccessTokenLifespan == time.Duration(0) {
|
||||
config.AccessTokenLifespan = schema.DefaultOpenIDConnectConfiguration.AccessTokenLifespan
|
||||
}
|
||||
|
@ -37,25 +68,9 @@ func validateOIDC(config *schema.OpenIDConnectConfiguration, validator *schema.S
|
|||
config.RefreshTokenLifespan = schema.DefaultOpenIDConnectConfiguration.RefreshTokenLifespan
|
||||
}
|
||||
|
||||
if config.MinimumParameterEntropy != 0 && config.MinimumParameterEntropy < 8 {
|
||||
validator.PushWarning(fmt.Errorf(errFmtOIDCServerInsecureParameterEntropy, config.MinimumParameterEntropy))
|
||||
}
|
||||
|
||||
if config.EnforcePKCE == "" {
|
||||
config.EnforcePKCE = schema.DefaultOpenIDConnectConfiguration.EnforcePKCE
|
||||
}
|
||||
|
||||
if config.EnforcePKCE != "never" && config.EnforcePKCE != "public_clients_only" && config.EnforcePKCE != "always" {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCEnforcePKCEInvalidValue, config.EnforcePKCE))
|
||||
}
|
||||
|
||||
validateOIDCOptionsCORS(config, validator)
|
||||
validateOIDCClients(config, validator)
|
||||
|
||||
if len(config.Clients) == 0 {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCNoClientsConfigured))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateOIDCOptionsCORS(config *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package validator
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
@ -20,7 +23,6 @@ func TestShouldRaiseErrorWhenInvalidOIDCServerConfiguration(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "abc",
|
||||
IssuerPrivateKey: "",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -37,7 +39,7 @@ func TestShouldNotRaiseErrorWhenCORSEndpointsValid(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
CORS: schema.OpenIDConnectCORSConfiguration{
|
||||
Endpoints: []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint},
|
||||
},
|
||||
|
@ -60,7 +62,7 @@ func TestShouldRaiseErrorWhenCORSEndpointsNotValid(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
CORS: schema.OpenIDConnectCORSConfiguration{
|
||||
Endpoints: []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint, "invalid_endpoint"},
|
||||
},
|
||||
|
@ -85,7 +87,7 @@ func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
EnforcePKCE: "invalid",
|
||||
},
|
||||
}
|
||||
|
@ -104,7 +106,7 @@ func TestShouldRaiseErrorWhenOIDCCORSOriginsHasInvalidValues(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
CORS: schema.OpenIDConnectCORSConfiguration{
|
||||
AllowedOrigins: utils.URLsFromStringSlice([]string{"https://example.com/", "https://site.example.com/subpath", "https://site.example.com?example=true", "*"}),
|
||||
AllowedOriginsFromClientRedirectURIs: true,
|
||||
|
@ -140,7 +142,7 @@ func TestShouldRaiseErrorWhenOIDCServerNoClients(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -320,7 +322,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: tc.Clients,
|
||||
},
|
||||
}
|
||||
|
@ -344,7 +346,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadScopes(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "good_id",
|
||||
|
@ -370,7 +372,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T)
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "good_id",
|
||||
|
@ -391,12 +393,66 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T)
|
|||
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'grant_types' must only have the values 'implicit', 'refresh_token', 'authorization_code', 'password', 'client_credentials' but one option is configured as 'bad_grant_type'")
|
||||
}
|
||||
|
||||
func TestShouldNotErrorOnCertificateValid(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerCertificateChain: mustParseX509CertificateChain(testCert1),
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(testKey1),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "good_id",
|
||||
Secret: "good_secret",
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"https://google.com/callback",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ValidateIdentityProviders(config, validator)
|
||||
|
||||
assert.Len(t, validator.Warnings(), 0)
|
||||
assert.Len(t, validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func TestShouldRaiseErrorOnCertificateNotValid(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerCertificateChain: mustParseX509CertificateChain(testCert1),
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(testKey2),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "good_id",
|
||||
Secret: "good_secret",
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"https://google.com/callback",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ValidateIdentityProviders(config, validator)
|
||||
|
||||
assert.Len(t, validator.Warnings(), 0)
|
||||
require.Len(t, validator.Errors(), 1)
|
||||
|
||||
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'issuer_private_key' does not appear to be the private key the certificate provided by option 'issuer_certificate_chain'")
|
||||
}
|
||||
|
||||
func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadResponseModes(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "good_id",
|
||||
|
@ -422,7 +478,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadUserinfoAlg(t *testing.T
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "key-material",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "good_id",
|
||||
|
@ -448,7 +504,7 @@ func TestValidateIdentityProvidersShouldRaiseWarningOnSecurityIssue(t *testing.T
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "abc",
|
||||
IssuerPrivateKey: "abc",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
MinimumParameterEntropy: 1,
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
|
@ -476,7 +532,7 @@ func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testi
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "hmac1",
|
||||
IssuerPrivateKey: "key2",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "client-with-invalid-secret",
|
||||
|
@ -514,7 +570,7 @@ func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidPublicClients(t *te
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "hmac1",
|
||||
IssuerPrivateKey: "key2",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "installed-app-client",
|
||||
|
@ -555,7 +611,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
|||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||
IssuerPrivateKey: "../../../README.md",
|
||||
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "a-client",
|
||||
|
@ -691,3 +747,111 @@ func TestValidateOIDCClientRedirectURIsSupportingPrivateUseURISchemes(t *testing
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
testCert1 = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5jCCAc6gAwIBAgIRAJZ+6KrHw95zIDgm2arCTCgwDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNMjIwOTA4MDIyNDQyWhcNMjMwOTA4MDIy
|
||||
NDQyWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAMAE7muDAJtLsV3WgOpjrZ1JD1RlhuSOa3V+4zo2NYFQSdZW18SZ
|
||||
fYYgUwLOleEy3VQ3N9MEFh/rWNHYHdsBjDvz/Q1EzAlXqthGd0Sic/UDYtrahrko
|
||||
jCSkZCQ5YVO9ivMRth6XdUlu7RHVYY3aSOWPx2wiw9cdN+e4p73W6KwyzT7ezbUD
|
||||
0Nng0Z7CNQTLHv3LBsLUODc4aVOvp2B4aAaw6cn990buKMvUuo2ge9gh0c5gIOM5
|
||||
dU7xOGAt7RzwCIHnG4CGAWPFuuS215ZeelgQr/9/fhtzDqSuBZw5f10vXnAyBwei
|
||||
vN6Kffj2RXB+koFwBguT84A6cfmxWllGNF0CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||
AQELBQADggEBAFvORjj7RGoIc3q0fv6QjuncZ0Mu1/24O0smCr6tq5d6RQBRpb1M
|
||||
jEsbTMLZErrHbyw/DWC75eJhW6T+6HiVTo6brBXkmDL+QGkLgRNOkZla6cnmIpmL
|
||||
bf9iPmmcThscQERgYZzNg19zqK8JAQU/6PgU/N6OXTL/mQQoB972ET9dUl7lGx1Q
|
||||
2l8XBe8t4QTp4t1xd3c4azxWvFNpzWBjC5eBWiVHLJmFXr4xpcnPFYFETOkvEqwt
|
||||
pMQ2x895BoLrep6b+g0xeF4pmmIQwA9KrUVr++gpYaRzytaOIYwcIPMzt9iLWKQe
|
||||
6ZSOrTVi8pPugYXp+LhVk/WI7r8EWtyADu0=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
testKey1 = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwATua4MAm0uxXdaA6mOtnUkPVGWG5I5rdX7jOjY1gVBJ1lbX
|
||||
xJl9hiBTAs6V4TLdVDc30wQWH+tY0dgd2wGMO/P9DUTMCVeq2EZ3RKJz9QNi2tqG
|
||||
uSiMJKRkJDlhU72K8xG2Hpd1SW7tEdVhjdpI5Y/HbCLD1x0357invdborDLNPt7N
|
||||
tQPQ2eDRnsI1BMse/csGwtQ4NzhpU6+nYHhoBrDpyf33Ru4oy9S6jaB72CHRzmAg
|
||||
4zl1TvE4YC3tHPAIgecbgIYBY8W65LbXll56WBCv/39+G3MOpK4FnDl/XS9ecDIH
|
||||
B6K83op9+PZFcH6SgXAGC5PzgDpx+bFaWUY0XQIDAQABAoIBAQClcdpHcglMxOwe
|
||||
kRpkWdwWAAQgUJXoSbnW86wu1NRHBfmInyyrrSBVN3aunXbQITZIQIdt3kB94haW
|
||||
P6KBt5Svd2saSqOOjSWb0SMkVOCaQ/+h19VqpcASNj4+Y94y+8ZD5ofHVfJtghDr
|
||||
Y7H5OhHDEZ3e0xlwODGaCyUkUY4KBv/oIlILoh4phbDYHkZH8AzDnEiyVE1JAWlN
|
||||
voAQysgSU7eEnNCi1S07jl5bY+MD3XpJkAfQsJYhqYT/qetStZ12PuXjpbIr3y53
|
||||
qjCrKeWTyDN+gOznyIGuiR6nvXeQAw/o9hZiah4RuHXTPs/3GAcRXcuMR0pbgJ+B
|
||||
yfX6eLK1AoGBAPKkJKPYJD2NHukAelNbT2OgeDIgJmfOIvLa73/x2hXvWwa4VwIC
|
||||
POuKXtT/a02J4pYMGlaKXfHgLmaW2HPObOIjpxqgRIswsiKS1AbaDtkWnhaS1/SJ
|
||||
oZ7Fk8DdX+1QT4J/fj/2uxRT0GhXdMxDpK7ekpmRE+APPCGhmOMgmWszAoGBAMqX
|
||||
Ts1RdGWgDxLi15rDqdqRBARJG7Om/xC2voXVmbAb4Q+QoNrNeiIAM2usuhrVuj5V
|
||||
c16m9fxswRNYqQBYyShDi5wp5a8UjfqDpzJdku2bmrBaL+XVq8PY+oTK6KS3ss8U
|
||||
CGQ8P6Phz5JGavn/nDMRZ4EwEWqbEMUqJAJlpmIvAoGAQ9Wj8LJyn0qew6FAkaFL
|
||||
dpzcPZdDZW35009l+a0RvWQnXJ+Yo5UglvEeRgoKY6kS0cQccOlKDl8QWdn+NZIW
|
||||
WrqA8y6vOwKoKoZGBIxd7k8mb0UqXtFDf/HYtuis8tmrAN7H2vYNo0czUphwrNKU
|
||||
bdcHwSsQFWns87IL3iO1AIUCgYBzmBX8jOePPN6c9hXzVoVKEshp8ZT+0uBilwLq
|
||||
tk/07lNiYDGH5woy8E5mt62QtjaIbpVfgoCEwUEBWutDKWXNtYypVDabyWyhbhEu
|
||||
abn2HX0L9smxqFNTcjCvKF/J7I74HQQUvVPKnIOlgMx1TOXBNcMLMXQekc/lz/+v
|
||||
5nQjPQKBgQDjdJABeiy9tU0tzLWUVc5QoQKnlfSJoFLis46REb1yHwU9OjTc05Wx
|
||||
5lAXdTjDmnelDdGWNWHjWOiKSkTxhvQD3jXriI5y8Sdxe3zS3ikYvbMbi97GJz0O
|
||||
5oyNJo6/froW1dLkJJWR8hg2PQbtoOo6l9HHSd91BnJJ4qFbq9ZrXQ==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
testKey2 = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA6z1LOg1ZCqb0lytXWZ+MRBpMHEXOoTOLYgfZXt1IYyE3Z758
|
||||
cyalk0NYQhY5cZDsXPYWPvAHiPMUxutWkoxFwby56S+AbIMa3/Is+ILrHRJs8Exn
|
||||
ZkpyrYFxPX12app2kErdmAkHSx0Z5/kuXiz96PHs8S8/ZbyZolLHzdfLtSzjvRm5
|
||||
Zue5iFzsf19NJz5CIBfv8g5lRwtE8wNJoRSpn1xq7fqfuA0weDNFPzjlNWRLy6aa
|
||||
rK7qJexRkmkCs4sLgyl+9NODYJpvmN8E1yhyC27E0joI6rBFVW7Ihv+cSPCdDzGp
|
||||
EWe81x3AeqAa3mjVqkiq4u4Z2i8JDgBaPboqJwIDAQABAoIBAAFdLZ58jVOefDSU
|
||||
L8F5R1rtvBs93GDa56f926jNJ6pLewLC+/2+757W+SAI+PRLntM7Kg3bXm/Q2QH+
|
||||
Q1Y+MflZmspbWCdI61L5GIGoYKyeers59i+FpvySj5GHtLQRiTZ0+Kv1AXHSDWBm
|
||||
9XneUOqU3IbZe0ifu1RRno72/VtjkGXbW8Mkkw+ohyGbIeTx/0/JQ6sSNZTT3Vk7
|
||||
8i4IXptq3HSF0/vqZuah8rShoeNq72pD1YLM9YPdL5by1QkDLnqATDiCpLBTCaNV
|
||||
I8sqYEun+HYbQzBj8ZACG2JVZpEEidONWQHw5BPWO95DSZYrVnEkuCqeH+u5vYt7
|
||||
CHuJ3AECgYEA+W3v5z+j91w1VPHS0VB3SCDMouycAMIUnJPAbt+0LPP0scUFsBGE
|
||||
hPAKddC54pmMZRQ2KIwBKiyWfCrJ8Xz8Yogn7fJgmwTHidJBr2WQpIEkNGlK3Dzi
|
||||
jXL2sh0yC7sHvn0DqiQ79l/e7yRbSnv2wrTJEczOOH2haD7/tBRyCYECgYEA8W+q
|
||||
E9YyGvEltnPFaOxofNZ8LHVcZSsQI5b6fc0iE7fjxFqeXPXEwGSOTwqQLQRiHn9b
|
||||
CfPmIG4Vhyq0otVmlPvUnfBZ2OK+tl5X2/mQFO3ROMdvpi0KYa994uqfJdSTaqLn
|
||||
jjoKFB906UFHnDQDLZUNiV1WwnkTglgLc+xrd6cCgYEAqqthyv6NyBTM3Tm2gcio
|
||||
Ra9Dtntl51LlXZnvwy3IkDXBCd6BHM9vuLKyxZiziGx+Vy90O1xI872cnot8sINQ
|
||||
Am+dur/tAEVN72zxyv0Y8qb2yfH96iKy9gxi5s75TnOEQgAygLnYWaWR2lorKRUX
|
||||
bHTdXBOiS58S0UzCFEslGIECgYBqkO4SKWYeTDhoKvuEj2yjRYyzlu28XeCWxOo1
|
||||
otiauX0YSyNBRt2cSgYiTzhKFng0m+QUJYp63/wymB/5C5Zmxi0XtWIDADpLhqLj
|
||||
HmmBQ2Mo26alQ5YkffBju0mZyhVzaQop1eZi8WuKFV1FThPlB7hc3E0SM5zv2Grd
|
||||
tQnOWwKBgQC40yZY0PcjuILhy+sIc0Wvh7LUA7taSdTye149kRvbvsCDN7Jh75lM
|
||||
USjhLXY0Nld2zBm9r8wMb81mXH29uvD+tDqqsICvyuKlA/tyzXR+QTr7dCVKVwu0
|
||||
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
)
|
||||
|
||||
func mustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||
block, _ := pem.Decode([]byte(data))
|
||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
||||
panic("not pem encoded")
|
||||
}
|
||||
|
||||
if block.Type != "RSA PRIVATE KEY" {
|
||||
panic("not private key")
|
||||
}
|
||||
|
||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func mustParseX509CertificateChain(data string) schema.X509CertificateChain {
|
||||
chain, err := schema.NewX509CertificateChain(data)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return *chain
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/model"
|
||||
|
@ -35,7 +36,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
ctx.Logger.Errorf("UserInfo Request failed with error: %+v", rfc)
|
||||
|
||||
if rfc.StatusCode() == http.StatusUnauthorized {
|
||||
rw.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription()))
|
||||
rw.Header().Set(fasthttp.HeaderWWWAuthenticate, fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription()))
|
||||
}
|
||||
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
|
||||
|
@ -90,9 +91,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
|
||||
claims["aud"] = audience
|
||||
|
||||
var (
|
||||
keyID, token string
|
||||
)
|
||||
var token string
|
||||
|
||||
ctx.Logger.Tracef("UserInfo Response with id '%s' on client with id '%s' is being sent with the following claims: %+v", requester.GetID(), clientID, claims)
|
||||
|
||||
|
@ -109,14 +108,8 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
|||
claims["jti"] = jti.String()
|
||||
claims["iat"] = time.Now().Unix()
|
||||
|
||||
if keyID, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().GetPublicKeyID(req.Context()); err != nil {
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHintf("Could not find the active JWK."))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
headers := &jwt.Headers{
|
||||
Extra: map[string]interface{}{"kid": keyID},
|
||||
Extra: map[string]any{"kid": ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID()},
|
||||
}
|
||||
|
||||
if token, _, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().Generate(req.Context(), claims, headers); err != nil {
|
||||
|
|
|
@ -37,8 +37,10 @@ func LoggerCtxPrintf(level logrus.Level) (logger *CtxPrintfLogger) {
|
|||
func InitializeLogger(config schema.LogConfiguration, log bool) error {
|
||||
setLevelStr(config.Level, log)
|
||||
|
||||
callerLevels := []logrus.Level{}
|
||||
var callerLevels []logrus.Level
|
||||
|
||||
stackLevels := []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel}
|
||||
|
||||
logrus.AddHook(logrus_stack.NewHook(callerLevels, stackLevels))
|
||||
|
||||
if config.Format == logFormatJSON {
|
||||
|
|
|
@ -9,19 +9,17 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
jose "gopkg.in/square/go-jose.v2"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// NewKeyManagerWithConfiguration when provided a schema.OpenIDConnectConfiguration creates a new KeyManager and adds an
|
||||
// initial key to the manager.
|
||||
func NewKeyManagerWithConfiguration(configuration *schema.OpenIDConnectConfiguration) (manager *KeyManager, err error) {
|
||||
func NewKeyManagerWithConfiguration(config *schema.OpenIDConnectConfiguration) (manager *KeyManager, err error) {
|
||||
manager = NewKeyManager()
|
||||
|
||||
_, _, err = manager.AddActivePrivateKeyData(configuration.IssuerPrivateKey)
|
||||
if err != nil {
|
||||
if _, err = manager.AddActiveJWK(config.IssuerCertificateChain, config.IssuerPrivateKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -30,174 +28,152 @@ func NewKeyManagerWithConfiguration(configuration *schema.OpenIDConnectConfigura
|
|||
|
||||
// NewKeyManager creates a new empty KeyManager.
|
||||
func NewKeyManager() (manager *KeyManager) {
|
||||
manager = new(KeyManager)
|
||||
manager.keys = map[string]*rsa.PrivateKey{}
|
||||
manager.keySet = new(jose.JSONWebKeySet)
|
||||
|
||||
return manager
|
||||
return &KeyManager{
|
||||
jwks: &jose.JSONWebKeySet{},
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy returns the RS256JWTStrategy.
|
||||
func (m *KeyManager) Strategy() (strategy *RS256JWTStrategy) {
|
||||
return m.strategy
|
||||
// Strategy returns the fosite jwt.JWTStrategy.
|
||||
func (m *KeyManager) Strategy() (strategy jwt.JWTStrategy) {
|
||||
if m.jwk == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.jwk.Strategy()
|
||||
}
|
||||
|
||||
// GetKeySet returns the joseJSONWebKeySet containing the rsa.PublicKey types.
|
||||
func (m *KeyManager) GetKeySet() (keySet *jose.JSONWebKeySet) {
|
||||
return m.keySet
|
||||
func (m *KeyManager) GetKeySet() (jwks *jose.JSONWebKeySet) {
|
||||
return m.jwks
|
||||
}
|
||||
|
||||
// GetActiveWebKey obtains the currently active jose.JSONWebKey.
|
||||
func (m *KeyManager) GetActiveWebKey() (webKey *jose.JSONWebKey, err error) {
|
||||
webKeys := m.keySet.Key(m.activeKeyID)
|
||||
if len(webKeys) == 1 {
|
||||
return &webKeys[0], nil
|
||||
// GetActiveJWK obtains the currently active jose.JSONWebKey.
|
||||
func (m *KeyManager) GetActiveJWK() (jwk *jose.JSONWebKey, err error) {
|
||||
if m.jwks == nil || m.jwk == nil {
|
||||
return nil, errors.New("could not obtain the active JWK from an improperly configured key manager")
|
||||
}
|
||||
|
||||
if len(webKeys) == 0 {
|
||||
jwks := m.jwks.Key(m.jwk.id)
|
||||
|
||||
if len(jwks) == 1 {
|
||||
return &jwks[0], nil
|
||||
}
|
||||
|
||||
if len(jwks) == 0 {
|
||||
return nil, errors.New("could not find a key with the active key id")
|
||||
}
|
||||
|
||||
return &webKeys[0], errors.New("multiple keys with the same key id")
|
||||
return nil, errors.New("multiple keys with the same key id")
|
||||
}
|
||||
|
||||
// GetActiveKeyID returns the key id of the currently active key.
|
||||
func (m *KeyManager) GetActiveKeyID() (keyID string) {
|
||||
return m.activeKeyID
|
||||
}
|
||||
|
||||
// GetActiveKey returns the rsa.PublicKey of the currently active key.
|
||||
func (m *KeyManager) GetActiveKey() (key *rsa.PublicKey, err error) {
|
||||
if key, ok := m.keys[m.activeKeyID]; ok {
|
||||
return &key.PublicKey, nil
|
||||
if m.jwk == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return nil, errors.New("failed to retrieve active public key")
|
||||
return m.jwk.id
|
||||
}
|
||||
|
||||
// GetActivePrivateKey returns the rsa.PrivateKey of the currently active key.
|
||||
func (m *KeyManager) GetActivePrivateKey() (key *rsa.PrivateKey, err error) {
|
||||
if key, ok := m.keys[m.activeKeyID]; ok {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
if m.jwk == nil {
|
||||
return nil, errors.New("failed to retrieve active private key")
|
||||
}
|
||||
|
||||
return m.jwk.key, nil
|
||||
}
|
||||
|
||||
// AddActivePrivateKeyData adds a rsa.PublicKey given the key in the PEM string format, then sets it to the active key.
|
||||
func (m *KeyManager) AddActivePrivateKeyData(data string) (key *rsa.PrivateKey, webKey *jose.JSONWebKey, err error) {
|
||||
ikey, err := utils.ParseX509FromPEM([]byte(data))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
|
||||
if key, ok = ikey.(*rsa.PrivateKey); !ok {
|
||||
return nil, nil, errors.New("key must be an RSA private key")
|
||||
}
|
||||
|
||||
webKey, err = m.AddActivePrivateKey(key)
|
||||
|
||||
return key, webKey, err
|
||||
}
|
||||
|
||||
// AddActivePrivateKey adds a rsa.PublicKey, then sets it to the active key.
|
||||
func (m *KeyManager) AddActivePrivateKey(key *rsa.PrivateKey) (webKey *jose.JSONWebKey, err error) {
|
||||
wk := jose.JSONWebKey{
|
||||
Key: &key.PublicKey,
|
||||
Algorithm: "RS256",
|
||||
Use: "sig",
|
||||
}
|
||||
|
||||
keyID, err := wk.Thumbprint(crypto.SHA1)
|
||||
if err != nil {
|
||||
// AddActiveJWK is used to add a cert and key pair.
|
||||
func (m *KeyManager) AddActiveJWK(chain schema.X509CertificateChain, key *rsa.PrivateKey) (jwk *JWK, err error) {
|
||||
// TODO: Add a mutex when implementing key rotation to be utilized here and in methods which retrieve the JWK or JWKS.
|
||||
if m.jwk, err = NewJWK(chain, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
strKeyID := strings.ToLower(fmt.Sprintf("%x", keyID))
|
||||
if len(strKeyID) >= 7 {
|
||||
// Shorten the key if it's greater than 7 to a length of exactly 7.
|
||||
strKeyID = strKeyID[0:6]
|
||||
}
|
||||
m.jwks.Keys = append(m.jwks.Keys, *m.jwk.JSONWebKey())
|
||||
|
||||
if _, ok := m.keys[strKeyID]; ok {
|
||||
return nil, fmt.Errorf("key id %s already exists", strKeyID)
|
||||
}
|
||||
|
||||
// TODO: Add Mutex here when implementing key rotation.
|
||||
wk.KeyID = strKeyID
|
||||
m.keySet.Keys = append(m.keySet.Keys, wk)
|
||||
m.keys[strKeyID] = key
|
||||
m.activeKeyID = strKeyID
|
||||
|
||||
m.strategy, err = NewRS256JWTStrategy(wk.KeyID, key)
|
||||
if err != nil {
|
||||
return &wk, err
|
||||
}
|
||||
|
||||
return &wk, nil
|
||||
return m.jwk, nil
|
||||
}
|
||||
|
||||
// NewRS256JWTStrategy returns a new RS256JWTStrategy.
|
||||
func NewRS256JWTStrategy(id string, key *rsa.PrivateKey) (strategy *RS256JWTStrategy, err error) {
|
||||
strategy = new(RS256JWTStrategy)
|
||||
strategy.JWTStrategy = new(jwt.RS256JWTStrategy)
|
||||
// JWTStrategy is a decorator struct for the fosite jwt.JWTStrategy.
|
||||
type JWTStrategy struct {
|
||||
jwt.JWTStrategy
|
||||
|
||||
strategy.SetKey(id, key)
|
||||
|
||||
return strategy, nil
|
||||
}
|
||||
|
||||
// RS256JWTStrategy is a decorator struct for the fosite RS256JWTStrategy.
|
||||
type RS256JWTStrategy struct {
|
||||
JWTStrategy *jwt.RS256JWTStrategy
|
||||
|
||||
keyID string
|
||||
id string
|
||||
}
|
||||
|
||||
// KeyID returns the key id.
|
||||
func (s RS256JWTStrategy) KeyID() (id string) {
|
||||
return s.keyID
|
||||
}
|
||||
|
||||
// SetKey sets the provided key id and key as the active key (this is what triggers fosite to use it).
|
||||
func (s *RS256JWTStrategy) SetKey(id string, key *rsa.PrivateKey) {
|
||||
s.keyID = id
|
||||
s.JWTStrategy.PrivateKey = key
|
||||
}
|
||||
|
||||
// Hash is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||
func (s *RS256JWTStrategy) Hash(ctx context.Context, in []byte) ([]byte, error) {
|
||||
return s.JWTStrategy.Hash(ctx, in)
|
||||
}
|
||||
|
||||
// GetSigningMethodLength is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||
func (s *RS256JWTStrategy) GetSigningMethodLength() int {
|
||||
return s.JWTStrategy.GetSigningMethodLength()
|
||||
}
|
||||
|
||||
// GetSignature is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||
func (s *RS256JWTStrategy) GetSignature(ctx context.Context, token string) (string, error) {
|
||||
return s.JWTStrategy.GetSignature(ctx, token)
|
||||
}
|
||||
|
||||
// Generate is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||
func (s *RS256JWTStrategy) Generate(ctx context.Context, claims jwt.MapClaims, header jwt.Mapper) (string, string, error) {
|
||||
return s.JWTStrategy.Generate(ctx, claims, header)
|
||||
}
|
||||
|
||||
// Validate is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||
func (s *RS256JWTStrategy) Validate(ctx context.Context, token string) (string, error) {
|
||||
return s.JWTStrategy.Validate(ctx, token)
|
||||
}
|
||||
|
||||
// Decode is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||
func (s *RS256JWTStrategy) Decode(ctx context.Context, token string) (*jwt.Token, error) {
|
||||
return s.JWTStrategy.Decode(ctx, token)
|
||||
func (s *JWTStrategy) KeyID() (id string) {
|
||||
return s.id
|
||||
}
|
||||
|
||||
// GetPublicKeyID is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||
func (s *RS256JWTStrategy) GetPublicKeyID(_ context.Context) (string, error) {
|
||||
return s.keyID, nil
|
||||
func (s *JWTStrategy) GetPublicKeyID(_ context.Context) (string, error) {
|
||||
return s.id, nil
|
||||
}
|
||||
|
||||
// NewJWK creates a new JWK.
|
||||
func NewJWK(chain schema.X509CertificateChain, key *rsa.PrivateKey) (j *JWK, err error) {
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf("JWK is not properly initialized: missing key")
|
||||
}
|
||||
|
||||
j = &JWK{
|
||||
key: key,
|
||||
chain: chain,
|
||||
}
|
||||
|
||||
jwk := &jose.JSONWebKey{
|
||||
Algorithm: "RS256",
|
||||
Use: "sig",
|
||||
Key: &key.PublicKey,
|
||||
}
|
||||
|
||||
var thumbprint []byte
|
||||
|
||||
if thumbprint, err = jwk.Thumbprint(crypto.SHA1); err != nil {
|
||||
return nil, fmt.Errorf("failed to calculate SHA1 thumbprint for certificate: %w", err)
|
||||
}
|
||||
|
||||
j.id = strings.ToLower(fmt.Sprintf("%x", thumbprint))
|
||||
|
||||
if len(j.id) >= 7 {
|
||||
j.id = j.id[:6]
|
||||
}
|
||||
|
||||
if len(j.id) >= 7 {
|
||||
j.id = j.id[:6]
|
||||
}
|
||||
|
||||
return j, nil
|
||||
}
|
||||
|
||||
// JWK is a utility wrapper for JSON Web Key's.
|
||||
type JWK struct {
|
||||
id string
|
||||
key *rsa.PrivateKey
|
||||
chain schema.X509CertificateChain
|
||||
}
|
||||
|
||||
// Strategy returns the relevant jwt.JWTStrategy for this JWT.
|
||||
func (j *JWK) Strategy() (strategy jwt.JWTStrategy) {
|
||||
return &JWTStrategy{id: j.id, JWTStrategy: &jwt.RS256JWTStrategy{PrivateKey: j.key}}
|
||||
}
|
||||
|
||||
// JSONWebKey returns the relevant *jose.JSONWebKey for this JWT.
|
||||
func (j *JWK) JSONWebKey() (jwk *jose.JSONWebKey) {
|
||||
jwk = &jose.JSONWebKey{
|
||||
Key: &j.key.PublicKey,
|
||||
KeyID: j.id,
|
||||
Algorithm: "RS256",
|
||||
Use: "sig",
|
||||
Certificates: j.chain.Certificates(),
|
||||
}
|
||||
|
||||
if len(jwk.Certificates) != 0 {
|
||||
jwk.CertificateThumbprintSHA1, jwk.CertificateThumbprintSHA256 = j.chain.Thumbprint(crypto.SHA1), j.chain.Thumbprint(crypto.SHA256)
|
||||
}
|
||||
|
||||
return jwk
|
||||
}
|
||||
|
|
|
@ -8,42 +8,37 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
)
|
||||
|
||||
func TestKeyManager_AddActiveKeyData(t *testing.T) {
|
||||
func TestKeyManager_AddActiveJWK(t *testing.T) {
|
||||
manager := NewKeyManager()
|
||||
assert.Nil(t, manager.strategy)
|
||||
assert.Nil(t, manager.jwk)
|
||||
assert.Nil(t, manager.Strategy())
|
||||
|
||||
key, wk, err := manager.AddActivePrivateKeyData(exampleIssuerPrivateKey)
|
||||
j, err := manager.AddActiveJWK(schema.X509CertificateChain{}, mustParseRSAPrivateKey(exampleIssuerPrivateKey))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, key)
|
||||
require.NotNil(t, wk)
|
||||
require.NotNil(t, manager.strategy)
|
||||
require.NotNil(t, j)
|
||||
require.NotNil(t, manager.jwk)
|
||||
require.NotNil(t, manager.Strategy())
|
||||
|
||||
thumbprint, err := wk.Thumbprint(crypto.SHA1)
|
||||
thumbprint, err := j.JSONWebKey().Thumbprint(crypto.SHA1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
kid := strings.ToLower(fmt.Sprintf("%x", thumbprint)[0:6])
|
||||
assert.Equal(t, manager.activeKeyID, kid)
|
||||
assert.Equal(t, kid, wk.KeyID)
|
||||
assert.Len(t, manager.keys, 1)
|
||||
assert.Len(t, manager.keySet.Keys, 1)
|
||||
assert.Contains(t, manager.keys, kid)
|
||||
kid := strings.ToLower(fmt.Sprintf("%x", thumbprint)[:6])
|
||||
assert.Equal(t, manager.jwk.id, kid)
|
||||
assert.Equal(t, kid, j.JSONWebKey().KeyID)
|
||||
assert.Len(t, manager.jwks.Keys, 1)
|
||||
|
||||
keys := manager.keySet.Key(kid)
|
||||
keys := manager.jwks.Key(kid)
|
||||
assert.Equal(t, keys[0].KeyID, kid)
|
||||
|
||||
privKey, err := manager.GetActivePrivateKey()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, privKey)
|
||||
|
||||
pubKey, err := manager.GetActiveKey()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, pubKey)
|
||||
|
||||
webKey, err := manager.GetActiveWebKey()
|
||||
webKey, err := manager.GetActiveJWK()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, webKey)
|
||||
|
||||
|
|
|
@ -14,17 +14,16 @@ import (
|
|||
|
||||
// NewOpenIDConnectProvider new-ups a OpenIDConnectProvider.
|
||||
func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, storageProvider storage.Provider) (provider OpenIDConnectProvider, err error) {
|
||||
provider = OpenIDConnectProvider{
|
||||
Fosite: nil,
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
provider.Store = NewOpenIDConnectStore(config, storageProvider)
|
||||
provider = OpenIDConnectProvider{
|
||||
Fosite: nil,
|
||||
Store: NewOpenIDConnectStore(config, storageProvider),
|
||||
}
|
||||
|
||||
composeConfiguration := &compose.Config{
|
||||
cconfig := &compose.Config{
|
||||
AccessTokenLifespan: config.AccessTokenLifespan,
|
||||
AuthorizeCodeLifespan: config.AuthorizeCodeLifespan,
|
||||
IDTokenLifespan: config.IDTokenLifespan,
|
||||
|
@ -50,19 +49,19 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, storage
|
|||
|
||||
strategy := &compose.CommonStrategy{
|
||||
CoreStrategy: compose.NewOAuth2HMACStrategy(
|
||||
composeConfiguration,
|
||||
cconfig,
|
||||
[]byte(utils.HashSHA256FromString(config.HMACSecret)),
|
||||
nil,
|
||||
),
|
||||
OpenIDConnectTokenStrategy: compose.NewOpenIDConnectStrategy(
|
||||
composeConfiguration,
|
||||
cconfig,
|
||||
key,
|
||||
),
|
||||
JWTStrategy: provider.KeyManager.Strategy(),
|
||||
}
|
||||
|
||||
provider.Fosite = compose.Compose(
|
||||
composeConfiguration,
|
||||
cconfig,
|
||||
provider.Store,
|
||||
strategy,
|
||||
PlainTextHasher{},
|
||||
|
@ -109,7 +108,7 @@ func (p OpenIDConnectProvider) Pairwise() bool {
|
|||
}
|
||||
|
||||
// Write writes data with herodot.JSONWriter.
|
||||
func (p OpenIDConnectProvider) Write(w http.ResponseWriter, r *http.Request, e interface{}, opts ...herodot.EncoderOptions) {
|
||||
func (p OpenIDConnectProvider) Write(w http.ResponseWriter, r *http.Request, e any, opts ...herodot.EncoderOptions) {
|
||||
p.herodot.Write(w, r, e, opts...)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
|
@ -20,17 +23,10 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_NotConfigured(t *testing
|
|||
assert.Nil(t, provider.Store)
|
||||
}
|
||||
|
||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_BadIssuerKey(t *testing.T) {
|
||||
_, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: "BAD KEY",
|
||||
}, nil)
|
||||
|
||||
assert.Error(t, err, "abc")
|
||||
}
|
||||
|
||||
func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing.T) {
|
||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
EnablePKCEPlainChallenge: true,
|
||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
|
@ -63,7 +59,8 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
|
|||
|
||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *testing.T) {
|
||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
|
@ -102,7 +99,8 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
|||
|
||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
|
@ -193,7 +191,8 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
|||
|
||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfiguration(t *testing.T) {
|
||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
|
@ -270,7 +269,8 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
|
|||
|
||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfigurationWithPlainPKCE(t *testing.T) {
|
||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||
EnablePKCEPlainChallenge: true,
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
|
@ -293,3 +293,21 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
|||
assert.Equal(t, "S256", disco.CodeChallengeMethodsSupported[0])
|
||||
assert.Equal(t, "plain", disco.CodeChallengeMethodsSupported[1])
|
||||
}
|
||||
|
||||
func mustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||
block, _ := pem.Decode([]byte(data))
|
||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
||||
panic("not pem encoded")
|
||||
}
|
||||
|
||||
if block.Type != "RSA PRIVATE KEY" {
|
||||
panic("not private key")
|
||||
}
|
||||
|
||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ import (
|
|||
|
||||
func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "myclient",
|
||||
|
@ -44,7 +45,8 @@ func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
|||
|
||||
func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "myclient",
|
||||
|
@ -76,7 +78,8 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
|||
}
|
||||
|
||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
||||
}, nil)
|
||||
|
||||
|
@ -103,7 +106,8 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
|||
}
|
||||
|
||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
||||
}, nil)
|
||||
|
||||
|
@ -114,7 +118,8 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
|||
|
||||
func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "myclient",
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
|
@ -21,21 +20,21 @@ func NewSession() (session *model.OpenIDSession) {
|
|||
return &model.OpenIDSession{
|
||||
DefaultSession: &openid.DefaultSession{
|
||||
Claims: &jwt.IDTokenClaims{
|
||||
Extra: map[string]interface{}{},
|
||||
Extra: map[string]any{},
|
||||
},
|
||||
Headers: &jwt.Headers{
|
||||
Extra: map[string]interface{}{},
|
||||
Extra: map[string]any{},
|
||||
},
|
||||
},
|
||||
Extra: map[string]interface{}{},
|
||||
Extra: map[string]any{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewSessionWithAuthorizeRequest uses details from an AuthorizeRequester to generate an OpenIDSession.
|
||||
func NewSessionWithAuthorizeRequest(issuer, kid, username string, amr []string, extra map[string]interface{},
|
||||
func NewSessionWithAuthorizeRequest(issuer, kid, username string, amr []string, extra map[string]any,
|
||||
authTime time.Time, consent *model.OAuth2ConsentSession, requester fosite.AuthorizeRequester) (session *model.OpenIDSession) {
|
||||
if extra == nil {
|
||||
extra = make(map[string]interface{})
|
||||
extra = map[string]any{}
|
||||
}
|
||||
|
||||
session = &model.OpenIDSession{
|
||||
|
@ -53,14 +52,14 @@ func NewSessionWithAuthorizeRequest(issuer, kid, username string, amr []string,
|
|||
AuthenticationMethodsReferences: amr,
|
||||
},
|
||||
Headers: &jwt.Headers{
|
||||
Extra: map[string]interface{}{
|
||||
Extra: map[string]any{
|
||||
"kid": kid,
|
||||
},
|
||||
},
|
||||
Subject: consent.Subject.UUID.String(),
|
||||
Username: username,
|
||||
},
|
||||
Extra: map[string]interface{}{},
|
||||
Extra: map[string]any{},
|
||||
ClientID: requester.GetClient().GetID(),
|
||||
ChallengeID: consent.ChallengeID,
|
||||
}
|
||||
|
@ -122,10 +121,8 @@ type Client struct {
|
|||
// KeyManager keeps track of all of the active/inactive rsa keys and provides them to services requiring them.
|
||||
// It additionally allows us to add keys for the purpose of key rotation in the future.
|
||||
type KeyManager struct {
|
||||
activeKeyID string
|
||||
keys map[string]*rsa.PrivateKey
|
||||
keySet *jose.JSONWebKeySet
|
||||
strategy *RS256JWTStrategy
|
||||
jwk *JWK
|
||||
jwks *jose.JSONWebKeySet
|
||||
}
|
||||
|
||||
// PlainTextHasher implements the fosite.Hasher interface without an actual hashing algo.
|
||||
|
|
|
@ -182,7 +182,7 @@ func (ekb ECDSAKeyBuilder) Build() (interface{}, error) {
|
|||
}
|
||||
|
||||
// ParseX509FromPEM parses PEM bytes and returns a PKCS key.
|
||||
func ParseX509FromPEM(data []byte) (key interface{}, err error) {
|
||||
func ParseX509FromPEM(data []byte) (key any, err error) {
|
||||
block, _ := pem.Decode(data)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to parse PEM block containing the key")
|
||||
|
|
Loading…
Reference in New Issue