Beispiel #1
1
/*
 * verify HMAC signature on JWT
 */
static apr_byte_t apr_jws_verify_rsa(apr_pool_t *pool, apr_jwt_t *jwt,
                                     apr_jwk_t *jwk, apr_jwt_error_t *err) {

    apr_byte_t rc = FALSE;

    /* get the OpenSSL digest function */
    const EVP_MD *digest = NULL;
    if ((digest = apr_jws_crypto_alg_to_evp(pool, jwt->header.alg, err)) == NULL)
        return FALSE;

    EVP_MD_CTX ctx;
    EVP_MD_CTX_init(&ctx);

    RSA * pubkey = RSA_new();

    BIGNUM * modulus = BN_new();
    BIGNUM * exponent = BN_new();

    BN_bin2bn(jwk->key.rsa->modulus, jwk->key.rsa->modulus_len, modulus);
    BN_bin2bn(jwk->key.rsa->exponent, jwk->key.rsa->exponent_len, exponent);

    pubkey->n = modulus;
    pubkey->e = exponent;

    EVP_PKEY* pRsaKey = EVP_PKEY_new();
    if (!EVP_PKEY_assign_RSA(pRsaKey, pubkey)) {
        pRsaKey = NULL;
        apr_jwt_error_openssl(err, "EVP_PKEY_assign_RSA");
        goto end;
    }

    if (apr_jws_signature_starts_with(pool, jwt->header.alg, "PS") == TRUE) {

        int status = 0;
        unsigned char *pDecrypted = apr_pcalloc(pool, jwt->signature.length);
        status = RSA_public_decrypt(jwt->signature.length, jwt->signature.bytes,
                                    pDecrypted, pubkey, RSA_NO_PADDING);
        if (status == -1) {
            apr_jwt_error_openssl(err, "RSA_public_decrypt");
            goto end;
        }

        unsigned char *pDigest = apr_pcalloc(pool, RSA_size(pubkey));
        unsigned int uDigestLen = RSA_size(pubkey);

        if (!EVP_DigestInit(&ctx, digest)) {
            apr_jwt_error_openssl(err, "EVP_DigestInit");
            goto end;
        }
        if (!EVP_DigestUpdate(&ctx, jwt->message, strlen(jwt->message))) {
            apr_jwt_error_openssl(err, "EVP_DigestUpdate");
            goto end;
        }
        if (!EVP_DigestFinal(&ctx, pDigest, &uDigestLen)) {
            apr_jwt_error_openssl(err, "wrong key? EVP_DigestFinal");
            goto end;
        }

        /* verify the data */
        status = RSA_verify_PKCS1_PSS(pubkey, pDigest, digest, pDecrypted,
                                      -2 /* salt length recovered from signature*/);
        if (status != 1) {
            apr_jwt_error_openssl(err, "RSA_verify_PKCS1_PSS");
            goto end;
        }

        rc = TRUE;

    } else if (apr_jws_signature_starts_with(pool, jwt->header.alg,
               "RS") == TRUE) {

        if (!EVP_VerifyInit_ex(&ctx, digest, NULL)) {
            apr_jwt_error_openssl(err, "EVP_VerifyInit_ex");
            goto end;
        }
        if (!EVP_VerifyUpdate(&ctx, jwt->message, strlen(jwt->message))) {
            apr_jwt_error_openssl(err, "EVP_VerifyUpdate");
            goto end;
        }
        if (!EVP_VerifyFinal(&ctx, (const unsigned char *) jwt->signature.bytes,
                             jwt->signature.length, pRsaKey)) {
            apr_jwt_error_openssl(err, "wrong key? EVP_VerifyFinal");
            goto end;
        }

        rc = TRUE;

    }

end:

    if (pRsaKey) {
        EVP_PKEY_free(pRsaKey);
    } else if (pubkey) {
        RSA_free(pubkey);
    }
    EVP_MD_CTX_cleanup(&ctx);

    return rc;
}
Beispiel #2
0
static apr_byte_t apr_jwk_parse_rsa_key(apr_pool_t *pool, int is_private_key,
		const char *kid, const char *filename, apr_jwk_t **j_jwk, apr_jwt_error_t *err) {
	BIO *input = NULL;
	apr_jwk_key_rsa_t *key = NULL;
	apr_byte_t rv = FALSE;

	if ((input = BIO_new(BIO_s_file())) == NULL) {
		apr_jwt_error_openssl(err, "BIO_new/BIO_s_file");
		goto end;
	}

	if (BIO_read_filename(input, filename) <= 0) {
		apr_jwt_error_openssl(err, "BIO_read_filename");
		goto end;
	}

	if (apr_jwk_rsa_bio_to_key(pool, input, &key, is_private_key, err) == FALSE)
		goto end;

	/* allocate memory for the JWK */
	*j_jwk = apr_pcalloc(pool, sizeof(apr_jwk_t));
	apr_jwk_t *jwk = *j_jwk;

	jwk->type = APR_JWK_KEY_RSA;
	jwk->key.rsa = key;

	if (kid != NULL) {
		jwk->kid = apr_pstrdup(pool, kid);
	} else {
		/* calculate a unique key identifier (kid) by fingerprinting the key params */
		// TODO: based just on sha1 hash of modulus "n" now..., could do this based on jwk->value.str
		if (apr_jwk_hash_and_base64urlencode(pool, key->modulus, key->modulus_len,
				&jwk->kid, err) == FALSE)
			goto end;
	}

	rv = TRUE;

end:

	if (input)
		BIO_free(input);

	return rv;
}
Beispiel #3
0
/*
 * hash a byte sequence with the specified algorithm
 */
apr_byte_t apr_jws_hash_bytes(apr_pool_t *pool, const char *s_digest,
                              const unsigned char *input, unsigned int input_len,
                              unsigned char **output, unsigned int *output_len, apr_jwt_error_t *err) {
    unsigned char md_value[EVP_MAX_MD_SIZE];

    EVP_MD_CTX ctx;
    EVP_MD_CTX_init(&ctx);

    const EVP_MD *evp_digest = NULL;
    if ((evp_digest = EVP_get_digestbyname(s_digest)) == NULL) {
        apr_jwt_error(err,
                      "no OpenSSL digest algorithm found for algorithm \"%s\"",
                      s_digest);
        return FALSE;
    }

    if (!EVP_DigestInit_ex(&ctx, evp_digest, NULL)) {
        apr_jwt_error_openssl(err, "EVP_DigestInit_ex");
        return FALSE;
    }
    if (!EVP_DigestUpdate(&ctx, input, input_len)) {
        apr_jwt_error_openssl(err, "EVP_DigestUpdate");
        return FALSE;
    }
    if (!EVP_DigestFinal_ex(&ctx, md_value, output_len)) {
        apr_jwt_error_openssl(err, "EVP_DigestFinal_ex");
        return FALSE;
    }

    EVP_MD_CTX_cleanup(&ctx);

    *output = apr_pcalloc(pool, *output_len);
    memcpy(*output, md_value, *output_len);

    return TRUE;
}
Beispiel #4
0
/*
 * verify HMAC signature on JWT
 */
static apr_byte_t apr_jws_verify_hmac(apr_pool_t *pool, apr_jwt_t *jwt,
                                      apr_jwk_t *jwk, apr_jwt_error_t *err) {

    if (jwk->type != APR_JWK_KEY_OCT) {
        apr_jwt_error(err,
                      "key type of provided JWK cannot be used for HMAC verification: %d",
                      jwk->type);
        return FALSE;
    }

    /* get the OpenSSL digest function */
    const EVP_MD *digest = NULL;
    if ((digest = apr_jws_crypto_alg_to_evp(pool, jwt->header.alg, err)) == NULL)
        return FALSE;

    /* prepare the message */
    unsigned char *msg = (unsigned char *) jwt->message;
    unsigned int msg_len = strlen(jwt->message);

    /* prepare the hash */
    unsigned int md_len = 0;
    unsigned char md[EVP_MAX_MD_SIZE];

    /* apply the HMAC function to the message with the provided key */
    if (!HMAC(digest, jwk->key.oct->k, jwk->key.oct->k_len, msg, msg_len, md,
              &md_len)) {
        apr_jwt_error_openssl(err, "HMAC");
        return FALSE;
    }

    /* check that the length of the hash matches what was provided to us in the signature */
    if (md_len != jwt->signature.length) {
        apr_jwt_error(err,
                      "calculated hash length (%d) differs from the length of the signature provided in the JWT (%d)", md_len, jwt->signature.length);
        return FALSE;
    }

    /* do a comparison of the provided hash value against calculated hash value */
    if (apr_jwt_memcmp(md, jwt->signature.bytes, md_len) == FALSE) {
        apr_jwt_error(err,
                      "calculated hash differs from the signature provided in the JWT");
        return FALSE;
    }

    /* all OK if we got to here */
    return TRUE;
}
Beispiel #5
0
/*
 * calculate a hash and base64url encode the result
 */
static apr_byte_t apr_jwk_hash_and_base64urlencode(apr_pool_t *pool,
		const unsigned char *input, const int input_len, char **output,
		apr_jwt_error_t *err) {

	unsigned int hash_len = SHA_DIGEST_LENGTH;
	unsigned char hash[SHA_DIGEST_LENGTH];

	// TODO: upgrade to SHA2?

	/* hash it */
	if (!SHA1(input, input_len, hash)) {
		apr_jwt_error_openssl(err, "SHA1");
		return FALSE;
	}

	/* base64url encode the key fingerprint */
	if (apr_jwt_base64url_encode(pool, output, (const char *) hash, hash_len, 0)
			<= 0) {
		apr_jwt_error(err, "apr_jwt_base64url_encode of hash failed");
		return FALSE;
	}

	return TRUE;
}
Beispiel #6
0
/*
 * verify EC signature on JWT
 */
static apr_byte_t apr_jws_verify_ec(apr_pool_t *pool, apr_jwt_t *jwt,
                                    apr_jwk_t *jwk, apr_jwt_error_t *err) {

    int nid = apr_jws_ec_alg_to_curve(jwt->header.alg);
    if (nid == -1) {
        apr_jwt_error(err,
                      "no OpenSSL Elliptic Curve identifier found for algorithm \"%s\"",
                      jwt->header.alg);
        return FALSE;
    }

    EC_GROUP *curve = EC_GROUP_new_by_curve_name(nid);
    if (curve == NULL) {
        apr_jwt_error(err,
                      "no OpenSSL Elliptic Curve found for algorithm \"%s\"",
                      jwt->header.alg);
        return FALSE;
    }

    apr_byte_t rc = FALSE;

    /* get the OpenSSL digest function */
    const EVP_MD *digest = NULL;
    if ((digest = apr_jws_crypto_alg_to_evp(pool, jwt->header.alg, err)) == NULL)
        return FALSE;

    EVP_MD_CTX ctx;
    EVP_MD_CTX_init(&ctx);

    EC_KEY * pubkey = EC_KEY_new();
    EC_KEY_set_group(pubkey, curve);

    BIGNUM * x = BN_new();
    BIGNUM * y = BN_new();

    BN_bin2bn(jwk->key.ec->x, jwk->key.ec->x_len, x);
    BN_bin2bn(jwk->key.ec->y, jwk->key.ec->y_len, y);

    if (!EC_KEY_set_public_key_affine_coordinates(pubkey, x, y)) {
        apr_jwt_error_openssl(err, "EC_KEY_set_public_key_affine_coordinates");
        return FALSE;
    }

    EVP_PKEY* pEcKey = EVP_PKEY_new();
    if (!EVP_PKEY_assign_EC_KEY(pEcKey, pubkey)) {
        pEcKey = NULL;
        apr_jwt_error_openssl(err, "EVP_PKEY_assign_EC_KEY");
        goto end;
    }

    ctx.pctx = EVP_PKEY_CTX_new(pEcKey, NULL);

    if (!EVP_PKEY_verify_init(ctx.pctx)) {
        apr_jwt_error_openssl(err, "EVP_PKEY_verify_init");
        goto end;
    }
    if (!EVP_VerifyInit_ex(&ctx, digest, NULL)) {
        apr_jwt_error_openssl(err, "EVP_VerifyInit_ex");
        goto end;
    }
    if (!EVP_VerifyUpdate(&ctx, jwt->message, strlen(jwt->message))) {
        apr_jwt_error_openssl(err, "EVP_VerifyUpdate");
        goto end;
    }
    if (!EVP_VerifyFinal(&ctx, (const unsigned char *) jwt->signature.bytes,
                         jwt->signature.length, pEcKey)) {
        apr_jwt_error_openssl(err, "wrong key? EVP_VerifyFinal");
        goto end;
    }

    rc = TRUE;

end:

    if (pEcKey) {
        EVP_PKEY_free(pEcKey);
    } else if (pubkey) {
        EC_KEY_free(pubkey);
    }
    EVP_MD_CTX_cleanup(&ctx);

    return rc;
}
Beispiel #7
0
/*
 * parse an RSA JWK in X.509 format (x5c)
 */
static apr_byte_t apr_jwk_parse_rsa_x5c(apr_pool_t *pool, json_t *json,
		apr_jwk_t *jwk, apr_jwt_error_t *err) {

	apr_byte_t rv = FALSE;

	/* get the "x5c" array element from the JSON object */
	json_t *v = json_object_get(json, "x5c");
	if (v == NULL) {
		apr_jwt_error(err, "JSON key \"%s\" could not be found", "x5c");
		return FALSE;
	}
	if (!json_is_array(v)) {
		apr_jwt_error(err,
				"JSON key \"%s\" was found but its value is not a JSON array",
				"x5c");
		return FALSE;
	}

	/* take the first element of the array */
	v = json_array_get(v, 0);
	if (v == NULL) {
		apr_jwt_error(err, "first element in JSON array is \"null\"");
		return FALSE;
	}
	if (!json_is_string(v)) {
		apr_jwt_error(err, "first element in array is not a JSON string");
		return FALSE;
	}

	const char *s_x5c = json_string_value(v);

	/* PEM-format it */
	const int len = 75;
	int i = 0;
	char *s = apr_psprintf(pool, "-----BEGIN CERTIFICATE-----\n");
	while (i < strlen(s_x5c)) {
		s = apr_psprintf(pool, "%s%s\n", s, apr_pstrndup(pool, s_x5c + i, len));
		i += len;
	}
	s = apr_psprintf(pool, "%s-----END CERTIFICATE-----\n", s);

	BIO *input = NULL;

	/* put it in BIO memory */
	if ((input = BIO_new(BIO_s_mem())) == NULL) {
		apr_jwt_error_openssl(err, "memory allocation BIO_new/BIO_s_mem");
		return FALSE;
	}

	if (BIO_puts(input, s) <= 0) {
		BIO_free(input);
		apr_jwt_error_openssl(err, "BIO_puts");
		return FALSE;
	}

	/* do the actual parsing */
	rv = apr_jwk_rsa_bio_to_key(pool, input, &jwk->key.rsa, FALSE, err);

	BIO_free(input);

	return rv;
}
Beispiel #8
0
/*
 * convert the RSA public key in the X.509 certificate in the BIO pointed to
 * by "input" to a JSON Web Key object
 */
static apr_byte_t apr_jwk_rsa_bio_to_key(apr_pool_t *pool, BIO *input,
		apr_jwk_key_rsa_t **jwk_key_rsa, int is_private_key,
		apr_jwt_error_t *err) {

	X509 *x509 = NULL;
	EVP_PKEY *pkey = NULL;
	apr_byte_t rv = FALSE;

	if (is_private_key) {
		/* get the private key struct from the BIO */
		if ((pkey = PEM_read_bio_PrivateKey(input, NULL, NULL, NULL)) == NULL) {
			apr_jwt_error_openssl(err, "PEM_read_bio_PrivateKey");
			goto end;
		}
	} else {
		/* read the X.509 struct */
		if ((x509 = PEM_read_bio_X509_AUX(input, NULL, NULL, NULL)) == NULL) {
			apr_jwt_error_openssl(err, "PEM_read_bio_X509_AUX");
			goto end;
		}
		/* get the public key struct from the X.509 struct */
		if ((pkey = X509_get_pubkey(x509)) == NULL) {
			apr_jwt_error_openssl(err, "X509_get_pubkey");
			goto end;
		}
	}

	/* allocate space */
	*jwk_key_rsa = apr_pcalloc(pool, sizeof(apr_jwk_key_rsa_t));
	apr_jwk_key_rsa_t *key = *jwk_key_rsa;

	/* get the RSA key from the public key struct */
	RSA *rsa = EVP_PKEY_get1_RSA(pkey);
	if (rsa == NULL) {
		apr_jwt_error_openssl(err, "EVP_PKEY_get1_RSA");
		goto end;
	}

	/* convert the modulus bignum in to a key/len */
	key->modulus_len = BN_num_bytes(rsa->n);
	key->modulus = apr_pcalloc(pool, key->modulus_len);
	BN_bn2bin(rsa->n, key->modulus);

	/* convert the exponent bignum in to a key/len */
	key->exponent_len = BN_num_bytes(rsa->e);
	key->exponent = apr_pcalloc(pool, key->exponent_len);
	BN_bn2bin(rsa->e, key->exponent);

	/* convert the private exponent bignum in to a key/len */
	if (rsa->d != NULL) {
		key->private_exponent_len = BN_num_bytes(rsa->d);
		key->private_exponent = apr_pcalloc(pool, key->private_exponent_len);
		BN_bn2bin(rsa->d, key->private_exponent);
	}

	RSA_free(rsa);

	rv = TRUE;

end:

	if (pkey)
		EVP_PKEY_free(pkey);
	if (x509)
		X509_free(x509);

	return rv;
}