Files
auth_service_c/src/jwt.c
T
2025-08-22 17:46:38 +07:00

265 lines
7.6 KiB
C

#include "jwt.h"
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <string.h>
// Удаляет символы '=' в конце строки (падинг)
static void remove_padding(const char *str) {
char *pos = strchr(str, '\0');
while (pos > str && *(pos - 1) == '=') {
*(--pos) = '\0';
}
}
// Преобразует Base64 в Base64URL
static void base64_to_base64url(char *str) {
for (char *p = str; *p; p++) {
if (*p == '+')
*p = '-';
else if (*p == '/')
*p = '_';
}
remove_padding(str); // Удаляем паддинг
}
// Преобразует Base64URL в Base64
static void base64url_to_base64(char *str) {
for (char *p = str; *p; p++) {
if (*p == '-')
*p = '+';
else if (*p == '_')
*p = '/';
}
// Добавляем паддинг для кратности 4
size_t len = strlen(str);
size_t pad = (4 - (len % 4)) % 4;
if (pad) {
char *end = str + len;
for (size_t i = 0; i < pad; i++) {
*(end++) = '=';
}
*end = '\0';
}
}
// Base64URL кодирование
char *jwt_base64url_encode(const unsigned char *input, const int length) {
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
BIO_write(bio, input, length);
BIO_flush(bio);
BUF_MEM *bufferPtr;
BIO_get_mem_ptr(bio, &bufferPtr);
const auto result = (char *) malloc(bufferPtr->length + 1);
memcpy(result, bufferPtr->data, bufferPtr->length);
result[bufferPtr->length] = '\0';
BIO_free_all(bio);
base64_to_base64url(result); // Преобразование в Base64URL
return result;
}
// Base64URL декодирование
unsigned char *jwt_base64url_decode(const char *input, int input_len,
int *output_len) {
if (input_len == 0) input_len = strlen(input);
// Создаем копию для преобразования
const auto input_copy = (char *) malloc(input_len + 1);
strncpy(input_copy, input, input_len);
input_copy[input_len] = '\0';
base64url_to_base64(input_copy); // Преобразование в Base64
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bio = BIO_new_mem_buf(input_copy, -1);
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
const auto buffer = (unsigned char *) malloc(input_len);
const int len = BIO_read(bio, buffer, input_len);
BIO_free_all(bio);
free(input_copy);
if (len <= 0) {
free(buffer);
*output_len = 0;
return nullptr;
}
*output_len = len;
return buffer;
}
// Создание JWT токена
char *jwt_createJWT(const char *header_json, const char *payload_json,
const char *secret) {
char *encodedHeader = jwt_base64url_encode(
(const unsigned char *) header_json, strlen(header_json));
char *encodedPayload = jwt_base64url_encode(
(const unsigned char *) payload_json, strlen(payload_json));
char *unsignedToken =
(char *) malloc(strlen(encodedHeader) + strlen(encodedPayload) + 2);
sprintf(unsignedToken, "%s.%s", encodedHeader, encodedPayload);
unsigned int digest_len;
unsigned char *digest = jwt_hmac_sha256(secret, strlen(secret), unsignedToken,
strlen(unsignedToken), &digest_len);
char *encodedSignature = jwt_base64url_encode(digest, digest_len);
char *jwt =
(char *) malloc(strlen(unsignedToken) + strlen(encodedSignature) + 2);
sprintf(jwt, "%s.%s", unsignedToken, encodedSignature);
free(encodedHeader);
free(encodedPayload);
free(unsignedToken);
free(digest);
free(encodedSignature);
return jwt;
}
// Генерация HMAC-SHA256
unsigned char *jwt_hmac_sha256(const char *key, int key_len, const char *data,
int data_len, unsigned int *digest_len) {
unsigned char *digest = (unsigned char *) malloc(EVP_MAX_MD_SIZE);
HMAC(EVP_sha256(), key, key_len, (const unsigned char *) data, data_len,
digest, digest_len);
return digest;
}
// Верификация JWT токена
bool jwt_verifyJWT(const char *token, const char *secret) {
const char *dot1 = strchr(token, '.');
const char *dot2 = dot1 ? strchr(dot1 + 1, '.') : nullptr;
if (!dot1 || !dot2) return false;
size_t header_len = dot1 - token;
size_t payload_len = dot2 - dot1 - 1;
size_t sig_len = strlen(dot2 + 1);
char *header = (char *) malloc(header_len + 1);
strncpy(header, token, header_len);
header[header_len] = '\0';
char *payload = (char *) malloc(payload_len + 1);
strncpy(payload, dot1 + 1, payload_len);
payload[payload_len] = '\0';
char *signature = strndup(dot2 + 1, sig_len);
char *signing_input = (char *) malloc(header_len + payload_len + 2);
sprintf(signing_input, "%s.%s", header, payload);
int expected_sig_len;
unsigned char *expected_sig_bin =
jwt_base64url_decode(signature, sig_len, &expected_sig_len);
unsigned int calculated_sig_len;
unsigned char *calculated_sig =
jwt_hmac_sha256(secret, strlen(secret), signing_input,
strlen(signing_input), &calculated_sig_len);
bool sig_valid =
(calculated_sig_len == expected_sig_len) &&
(memcmp(calculated_sig, expected_sig_bin, calculated_sig_len) == 0);
bool exp_valid = true;
if (sig_valid) {
int payload_bin_len;
unsigned char *payload_bin =
jwt_base64url_decode(payload, payload_len, &payload_bin_len);
char *payload_str = (char *) malloc(payload_bin_len + 1);
memcpy(payload_str, payload_bin, payload_bin_len);
payload_str[payload_bin_len] = '\0';
cJSON *json_payload = cJSON_Parse(payload_str);
if (json_payload) {
cJSON *exp = cJSON_GetObjectItemCaseSensitive(json_payload, "exp");
if (cJSON_IsNumber(exp)) {
time_t now = time(nullptr);
if (now >= (time_t) exp->valuedouble) {
exp_valid = false;
}
}
cJSON_Delete(json_payload);
}
free(payload_bin);
free(payload_str);
}
free(header);
free(payload);
free(signature);
free(signing_input);
free(expected_sig_bin);
free(calculated_sig);
return sig_valid && exp_valid;
}
// Получение payload из токена
cJSON *jwt_get_payload(const char *token) {
const char *dot1 = strchr(token, '.');
const char *dot2 = dot1 ? strchr(dot1 + 1, '.') : nullptr;
if (!dot1 || !dot2) return nullptr;
size_t payload_len = dot2 - dot1 - 1;
char *payload = (char *) malloc(payload_len + 1);
strncpy(payload, dot1 + 1, payload_len);
payload[payload_len] = '\0';
int payload_bin_len;
unsigned char *payload_bin =
jwt_base64url_decode(payload, payload_len, &payload_bin_len);
char *payload_str = (char *) malloc(payload_bin_len + 1);
memcpy(payload_str, payload_bin, payload_bin_len);
payload_str[payload_bin_len] = '\0';
cJSON *json_payload = cJSON_Parse(payload_str);
free(payload);
free(payload_bin);
free(payload_str);
return json_payload;
}
// Получение header из токена
cJSON *jwt_get_header(const char *token) {
const char *dot1 = strchr(token, '.');
if (!dot1) return nullptr;
size_t header_len = dot1 - token;
char *header = (char *) malloc(header_len + 1);
strncpy(header, token, header_len);
header[header_len] = '\0';
int header_bin_len;
unsigned char *header_bin =
jwt_base64url_decode(header, header_len, &header_bin_len);
char *header_str = (char *) malloc(header_bin_len + 1);
memcpy(header_str, header_bin, header_bin_len);
header_str[header_bin_len] = '\0';
cJSON *json_header = cJSON_Parse(header_str);
free(header);
free(header_bin);
free(header_str);
return json_header;
}