diff --git a/.travis.yml b/.travis.yml
index b548d08..5e15c3a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,7 +30,7 @@ install:
- make
- sudo make install
before_script:
- - pip install pyjwt requests
+ - pip install pyjwt requests cryptography
- chmod ugo+x tests/debian_tests.sh
- cd tests
script: ./debian_tests.sh
diff --git a/mod_authnz_jwt.c b/mod_authnz_jwt.c
index 043ab04..777bb58 100644
--- a/mod_authnz_jwt.c
+++ b/mod_authnz_jwt.c
@@ -48,6 +48,7 @@
#define DEFAULT_FORM_USERNAME "user"
#define DEFAULT_FORM_PASSWORD "password"
#define DEFAULT_ATTRIBUTE_USERNAME "user"
+#define DEFAULT_SIGNATURE_ALGORITHM "HS256"
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIGURATION STRUCTURE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
@@ -143,7 +144,7 @@ static int create_token(request_rec *r, char** token_str, const char* username);
static int auth_jwt_authn_with_token(request_rec *r);
static void get_encode_key(request_rec* r, const char* algorithm, unsigned char* key);
-static void get_decode_key(request_rec* r, const char* algorithm, unsigned char* key);
+static void get_decode_key(request_rec* r, unsigned char* key);
static int token_check(request_rec *r, jwt_t **jwt, const char *token, const unsigned char *key);
static int token_decode(jwt_t **jwt, const char* token, const unsigned char *key);
static int token_new(jwt_t **jwt);
@@ -152,11 +153,12 @@ static long token_get_claim_int(jwt_t *token, const char* claim);
static int token_add_claim(jwt_t *jwt, const char *claim, const char *val);
static int token_add_claim_int(jwt_t *jwt, const char *claim, long val);
static void token_free(jwt_t *token);
-static int token_set_alg(jwt_t *jwt, jwt_alg_t alg, const unsigned char *key);
+static int token_set_alg(request_rec *r, jwt_t *jwt, const char* alg, const unsigned char *key);
static char *token_encode_str(jwt_t *jwt);
static char** token_get_claim_array_of_string(request_rec* r, jwt_t *token, const char* claim, int* len);
static json_t* token_get_claim_array(request_rec* r, jwt_t *token, const char* claim);
static json_t* token_get_claim_json(request_rec* r, jwt_t *token, const char* claim);
+static const char* token_get_alg(jwt_t *jwt);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECLARE DIRECTIVES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
@@ -309,7 +311,7 @@ static void* get_config_value(request_rec *r, jwt_directive directive){
}else if(sconf->signature_algorithm){
value = (void*)sconf->signature_algorithm;
}else{
- return NULL;
+ return DEFAULT_SIGNATURE_ALGORITHM;
}
break;
case dir_signature_shared_secret:
@@ -764,37 +766,12 @@ static int create_token(request_rec *r, char** token_str, const char* username){
int* exp_delay_ptr = (int*)get_config_value(r, dir_exp_delay);
int* nbf_delay_ptr = (int*)get_config_value(r, dir_nbf_delay);
-
-
- jwt_alg_t algorithm;
- if(!strcmp(signature_algorithm, "HS512")){
- algorithm = JWT_ALG_HS512;
- }else if(!strcmp(signature_algorithm, "HS384")){
- algorithm = JWT_ALG_HS384;
- }else if(!strcmp(signature_algorithm, "HS256")){
- algorithm = JWT_ALG_HS256;
- }else if(!strcmp(signature_algorithm, "RS512")){
- algorithm = JWT_ALG_RS512;
- }else if(!strcmp(signature_algorithm, "RS384")){
- algorithm = JWT_ALG_RS384;
- }else if(!strcmp(signature_algorithm, "RS256")){
- algorithm = JWT_ALG_RS256;
- }else if(!strcmp(signature_algorithm, "ES512")){
- algorithm = JWT_ALG_ES512;
- }else if(!strcmp(signature_algorithm, "ES384")){
- algorithm = JWT_ALG_ES384;
- }else if(!strcmp(signature_algorithm, "ES256")){
- algorithm = JWT_ALG_ES256;
- }else{
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55304)
- "Unknown algorithm %s", signature_algorithm);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
-
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55305)
"auth_jwt create_token: using algorithm %s...", signature_algorithm);
- token_set_alg(token, algorithm, sign_key);
+ if(token_set_alg(r, token, signature_algorithm, sign_key)!=0){
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
time_t now = time(NULL);
@@ -952,11 +929,9 @@ static int auth_jwt_authn_with_token(request_rec *r){
char* authorization_header = (char*)apr_table_get( r->headers_in, "Authorization");
char* token_str;
- char* signature_algorithm = (char*)get_config_value(r, dir_signature_algorithm);
-
-
unsigned char key[MAX_KEY_LEN] = { 0 };
- get_decode_key(r, signature_algorithm, key);
+
+ get_decode_key(r, key);
if(strlen((const char*)key)==0){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55403)
@@ -976,11 +951,15 @@ static int auth_jwt_authn_with_token(request_rec *r){
token_str = authorization_header+7;
jwt_t* token;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55405)
- "auth_jwt authn: checking signature with algorithm %s and fields correctness...", signature_algorithm);
+ "auth_jwt authn: checking signature and fields correctness...");
rv = token_check(r, &token, token_str, key);
+
if(OK == rv){
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55406)
"auth_jwt authn: signature is correct");
+ const char* found_alg = token_get_alg(token);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55405)
+ "auth_jwt authn: algorithm found is %s", found_alg);
const char* attribute_username = (const char*)get_config_value(r, dir_attribute_username);
char* maybe_user = (char *)token_get_claim(token, attribute_username);
if(maybe_user == NULL){
@@ -1014,11 +993,6 @@ static int auth_jwt_authn_with_token(request_rec *r){
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TOKEN OPERATIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
static void get_encode_key(request_rec *r, const char* signature_algorithm, unsigned char* key){
- if(!signature_algorithm){
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55500)
- "Signature algorithm is NULL. This error should not happen since a default algorithm is set");
- return;
- }
if(strcmp(signature_algorithm, "HS512")==0 || strcmp(signature_algorithm, "HS384")==0 || strcmp(signature_algorithm, "HS256")==0){
char* signature_shared_secret = (char*)get_config_value(r, dir_signature_shared_secret);
@@ -1060,30 +1034,26 @@ static void get_encode_key(request_rec *r, const char* signature_algorithm, unsi
}
}
-static void get_decode_key(request_rec *r, const char* signature_algorithm, unsigned char* key){
- if(!signature_algorithm){
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55506)
- "Signature algorithm is NULL. This error should not happen since a default algorithm is set");
+static void get_decode_key(request_rec *r, unsigned char* key){
+ char* signature_public_key_file = (char*)get_config_value(r, dir_signature_public_key_file);
+ char* signature_shared_secret = (char*)get_config_value(r, dir_signature_shared_secret);
+
+ if(!signature_shared_secret && !signature_public_key_file){
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55507)
+ "You must specify either AuthJWTSignatureSharedSecret directive or AuthJWTSignaturePublicKeyFile directive in configuration for decoding process");
return;
- }
+ }
- if(strcmp(signature_algorithm, "HS512")==0 || strcmp(signature_algorithm, "HS384")==0 || strcmp(signature_algorithm, "HS256")==0){
- char* signature_shared_secret = (char*)get_config_value(r, dir_signature_shared_secret);
- if(!signature_shared_secret){
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55507)
- "You must specify AuthJWTSignatureSharedSecret directive in configuration with algorithm %s", signature_algorithm);
- return;
- }
- strcpy((char*)key,(const char*)signature_shared_secret);
- }
- else if(strcmp(signature_algorithm, "RS512")==0 || strcmp(signature_algorithm, "RS384")==0 || strcmp(signature_algorithm, "RS256")==0 ||
- strcmp(signature_algorithm, "ES512")==0 || strcmp(signature_algorithm, "ES384")==0 || strcmp(signature_algorithm, "ES256")==0){
- char* signature_public_key_file = (char*)get_config_value(r, dir_signature_public_key_file);
- if(!signature_public_key_file){
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55508)
- "You must specify AuthJWTSignaturePublicKeyFile directive in configuration with algorithm %s", signature_algorithm);
- return;
- }
+ if(signature_shared_secret && signature_public_key_file){
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55507)
+ "Conflict in configuration: you must specify either AuthJWTSignatureSharedSecret directive or AuthJWTSignaturePublicKeyFile directive but not both in the same block");
+ return;
+ }
+
+ if(signature_shared_secret){
+ strcpy((char*)key,(const char*)signature_shared_secret);
+ }
+ else if(signature_public_key_file){
apr_status_t rv;
apr_file_t* key_fd = NULL;
rv = apr_file_open(&key_fd, signature_public_key_file, APR_READ, APR_OS_DEFAULT, r->pool);
@@ -1100,11 +1070,7 @@ static void get_decode_key(request_rec *r, const char* signature_algorithm, unsi
return;
}
apr_file_close(key_fd);
- } else {
- //unknown algorithm
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55511)
- "Unknown algorithm %s", signature_algorithm);
- }
+ }
}
static int token_new(jwt_t **jwt){
@@ -1277,8 +1243,58 @@ static json_t* token_get_claim_json(request_rec *r, jwt_t *token, const char* cl
return json;
}
-static int token_set_alg(jwt_t *jwt, jwt_alg_t alg, const unsigned char *key){
- return jwt_set_alg(jwt, alg, key, strlen((const char*)key));
+static int token_set_alg(request_rec *r, jwt_t *jwt, const char* signature_algorithm, const unsigned char *key){
+ jwt_alg_t algorithm;
+ if(!strcmp(signature_algorithm, "HS512")){
+ algorithm = JWT_ALG_HS512;
+ }else if(!strcmp(signature_algorithm, "HS384")){
+ algorithm = JWT_ALG_HS384;
+ }else if(!strcmp(signature_algorithm, "HS256")){
+ algorithm = JWT_ALG_HS256;
+ }else if(!strcmp(signature_algorithm, "RS512")){
+ algorithm = JWT_ALG_RS512;
+ }else if(!strcmp(signature_algorithm, "RS384")){
+ algorithm = JWT_ALG_RS384;
+ }else if(!strcmp(signature_algorithm, "RS256")){
+ algorithm = JWT_ALG_RS256;
+ }else if(!strcmp(signature_algorithm, "ES512")){
+ algorithm = JWT_ALG_ES512;
+ }else if(!strcmp(signature_algorithm, "ES384")){
+ algorithm = JWT_ALG_ES384;
+ }else if(!strcmp(signature_algorithm, "ES256")){
+ algorithm = JWT_ALG_ES256;
+ }else{
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55304)
+ "Unknown algorithm %s", signature_algorithm);
+ return 1;
+ }
+ return jwt_set_alg(jwt, algorithm, key, strlen((const char*)key));
+}
+
+static const char* token_get_alg(jwt_t *jwt){
+ jwt_alg_t algorithm = jwt_get_alg(jwt);
+ switch(algorithm){
+ case JWT_ALG_HS256:
+ return "HS256";
+ case JWT_ALG_HS384:
+ return "HS384";
+ case JWT_ALG_HS512:
+ return "HS512";
+ case JWT_ALG_RS256:
+ return "RS256";
+ case JWT_ALG_RS384:
+ return "RS384";
+ case JWT_ALG_RS512:
+ return "RS512";
+ case JWT_ALG_ES256:
+ return "ES256";
+ case JWT_ALG_ES384:
+ return "ES384";
+ case JWT_ALG_ES512:
+ return "ES512";
+ default:
+ return NULL;
+ }
}
static void token_free(jwt_t *token){
diff --git a/tests/apache_hmac.conf b/tests/apache_hmac.conf
deleted file mode 100644
index 2916657..0000000
--- a/tests/apache_hmac.conf
+++ /dev/null
@@ -1,36 +0,0 @@
-
- ServerName hmac.testjwt.local
- DocumentRoot /var/www/testjwt/
-
- AuthJWTSignatureAlgorithm HS256
- AuthJWTSignatureSharedSecret secret
- AuthJWTExpDelay 1800
- AuthJWTNbfDelay 0
- AuthJWTIss hmac.testjwt.local
- AuthJWTAud tests
- AuthJWTLeeway 10
-
-
- AllowOverride None
- Options -Indexes
- Require all granted
-
-
-
- AllowOverride None
- Options -Indexes
- AuthType jwt
- AuthName "private area"
- Require valid-user
-
-
-
-
- SetHandler jwt-login-handler
- AuthJWTProvider file
- AuthUserFile /var/www/jwt.htpasswd
-
-
- ErrorLog ${APACHE_LOG_DIR}/error.log
- CustomLog ${APACHE_LOG_DIR}/access.log combined
-
diff --git a/tests/apache_jwt.conf b/tests/apache_jwt.conf
new file mode 100644
index 0000000..8528f04
--- /dev/null
+++ b/tests/apache_jwt.conf
@@ -0,0 +1,61 @@
+
+ ServerName testjwt.local
+ DocumentRoot /var/www/testjwt/
+
+ AuthJWTExpDelay 1800
+ AuthJWTNbfDelay 0
+ AuthJWTIss testjwt.local
+ AuthJWTAud tests
+ AuthJWTLeeway 10
+
+ LogLevel auth_jwt:debug
+ RewriteEngine On
+
+ Alias "/hmac_secured" "/var/www/testjwt"
+ Alias "/rsa_secured" "/var/www/testjwt"
+ Alias "/ec_secured" "/var/www/testjwt"
+
+
+ AllowOverride None
+ Options -Indexes
+ Require all granted
+
+
+
+ AuthJWTSignatureSharedSecret secret
+ AllowOverride None
+ Options -Indexes
+ AuthType jwt
+ AuthName "private area"
+ Require valid-user
+
+
+
+ AuthJWTSignaturePublicKeyFile /tmp/rsa-pub.pem
+ AllowOverride None
+ Options -Indexes
+ AuthType jwt
+ AuthName "private area"
+ Require valid-user
+
+
+
+ AuthJWTSignaturePublicKeyFile /tmp/ec-pub.pem
+ AllowOverride None
+ Options -Indexes
+ AuthType jwt
+ AuthName "private area"
+ Require valid-user
+
+
+
+ AuthJWTSignatureAlgorithm HS256
+ AuthJWTSignatureSharedSecret secret
+ SetHandler jwt-login-handler
+ AuthJWTProvider file
+ AuthUserFile /var/www/jwt.htpasswd
+
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
diff --git a/tests/debian_tests.sh b/tests/debian_tests.sh
index de426d7..714ff64 100755
--- a/tests/debian_tests.sh
+++ b/tests/debian_tests.sh
@@ -1,17 +1,26 @@
#!/bin/bash
set -ev
-sudo cp apache_hmac.conf /etc/apache2/sites-available/
-sudo mkdir -p /var/www/testjwt/jwt_secured/
-sudo touch /var/www/testjwt/jwt_secured/index.html
+sudo cp apache_jwt.conf /etc/apache2/sites-available/
sudo cp jwt.htpasswd /var/www/jwt.htpasswd
+sudo mkdir -p /var/www/testjwt/
+sudo touch /var/www/testjwt/index.html
-if ! sudo a2query -s apache_hmac > /dev/null; then
- sudo a2ensite apache_hmac
- sudo service apache2 restart
+openssl ecparam -name secp256k1 -genkey -noout -out /tmp/ec-priv.pem
+openssl ec -in /tmp/ec-priv.pem -pubout -out /tmp/ec-pub.pem
+
+openssl genpkey -algorithm RSA -out /tmp/rsa-priv.pem -pkeyopt rsa_keygen_bits:4096
+openssl rsa -pubout -in /tmp/rsa-priv.pem -out /tmp/rsa-pub.pem
+
+if ! sudo a2query -m rewrite > /dev/null; then
+ sudo a2enmod rewrite
+fi
+if ! sudo a2query -s apache_jwt > /dev/null; then
+ sudo a2ensite apache_jwt
fi
+sudo service apache2 restart
-if ! grep -q "hmac.testjwt.local" /etc/hosts; then
- echo "127.0.0.1 hmac.testjwt.local" | sudo tee --append /etc/hosts > /dev/null
+if ! grep -q "testjwt.local" /etc/hosts; then
+ echo "127.0.0.1 testjwt.local" | sudo tee --append /etc/hosts > /dev/null
fi
python3 -m unittest discover . -v -f --locals
diff --git a/tests/test_jwt.py b/tests/test_jwt.py
index af9a5ad..93d3875 100644
--- a/tests/test_jwt.py
+++ b/tests/test_jwt.py
@@ -8,8 +8,10 @@
class TestJWT(unittest.TestCase):
- HMAC_SECURED_URL = "http://hmac.testjwt.local/jwt_secured/"
- LOGIN_PATH = "http://hmac.testjwt.local/jwt_login"
+ HMAC_SECURED_URL = "http://testjwt.local/hmac_secured"
+ RSA_SECURED_URL = "http://testjwt.local/rsa_secured"
+ EC_SECURED_URL = "http://testjwt.local/ec_secured"
+ LOGIN_PATH = "http://testjwt.local/jwt_login"
USERNAME = "test"
PASSWORD = "test"
HMAC_SHARED_SECRET = "secret"
@@ -18,11 +20,10 @@ class TestJWT(unittest.TestCase):
PASSWORD_FIELD = "password"
JWT_EXPDELAY = 1800
JWT_NBF_DELAY = 0
- JWT_ISS = "hmac.testjwt.local"
+ JWT_ISS = "testjwt.local"
JWT_AUD = "tests"
JWT_LEEWAY = 10
- ALGORITHMS = ["HS256", "HS384", "HS512"]
- # ALGORITHMS = ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
+ ALGORITHMS = ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
@classmethod
def with_all_algorithms(cls, algorithms=None):
@@ -35,12 +36,12 @@ def handler(_self):
if alg in ("HS256", "HS384", "HS512"):
key = cls.HMAC_SHARED_SECRET
secured_url = cls.HMAC_SECURED_URL
- #elif alg in ("RS256", "RS384", "RS512"):
- # key = ""
- # secured_url = ""
- #elif alg in ("ES256", "ES384", "ES512"):
- # key = ""
- # secured_url = ""
+ elif alg in ("RS256", "RS384", "RS512"):
+ key = open("/tmp/rsa-priv.pem").read()
+ secured_url = cls.RSA_SECURED_URL
+ elif alg in ("ES256", "ES384", "ES512"):
+ key = open("/tmp/ec-priv.pem").read()
+ secured_url = cls.EC_SECURED_URL
with _self.subTest(alg=alg, key=key):
func(_self, alg, key, secured_url)
return handler