如何使用cjose库实现JSON Web Encryption?求IETF-JOSE示例指引
Hey there! I’ve used the cjose library for JWE implementations a few times, so let’s break this down clearly—covering setup, step-by-step code examples, IETF-JOSE compliant formats, and key best practices.
First, make sure you have the cjose library set up on your system. Here’s how to install it for common platforms:
- Debian/Ubuntu: Run
sudo apt-get install libcjose-dev - RHEL/CentOS: Run
sudo yum install libcjose-devel - From source: Clone the cjose repository, then build with
cmake . && make && sudo make install
Per the IETF JOSE specification, JWE supports two main serialization formats:
- Compact: A single string with 5 dot-separated Base64URL-encoded components
- JSON: A structured JSON object with explicit fields for each JWE part
We’ll cover both formats in the examples below.
Let’s walk through a typical use case: encrypting a payload with an RSA public key, then decrypting it with the matching private key.
3.1 Encrypt a Payload (JWE Compact Format)
This C snippet generates a JWE using RSA-OAEP (an IETF-recommended asymmetric encryption algorithm):
#include <stdio.h> #include <stdlib.h> #include <cjose/cjose.h> int main() { // Initialize cjose context cjose_err err; cjose_ctx *ctx = cjose_ctx_new(NULL, &err); if (!ctx) { fprintf(stderr, "Failed to create context: %s\n", err.message); return 1; } // Load your RSA public key (replace with your actual PEM string) const char *pub_key_pem = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----"; cjose_jwk_t *pub_key = cjose_jwk_from_pem(pub_key_pem, strlen(pub_key_pem), &err); if (!pub_key) { fprintf(stderr, "Failed to load public key: %s\n", err.message); cjose_ctx_free(ctx); return 1; } // Define JWE protected header (alg = RSA-OAEP, enc = A256GCM) cjose_header_t *header = cjose_header_new(); cjose_header_set_param(header, "alg", cjose_string_create("RSA-OAEP", NULL)); cjose_header_set_param(header, "enc", cjose_string_create("A256GCM", NULL)); // Payload to encrypt (example JSON data) const char *payload = "{\"user_id\":123,\"role\":\"admin\"}"; // Encrypt the payload into a JWE object cjose_jwe_t *jwe = cjose_jwe_encrypt(ctx, pub_key, header, payload, strlen(payload), &err); if (!jwe) { fprintf(stderr, "Encryption failed: %s\n", err.message); cjose_jwk_free(pub_key); cjose_ctx_free(ctx); return 1; } // Serialize JWE to compact string format cjose_string_t *jwe_compact = cjose_jwe_serialize(jwe, CJOSE_SERIALIZE_COMPACT, &err); if (jwe_compact) { printf("JWE Compact Format:\n%s\n", jwe_compact->data); cjose_string_free(jwe_compact); } // Clean up resources cjose_jwe_free(jwe); cjose_jwk_free(pub_key); cjose_ctx_free(ctx); return 0; }
3.2 Decrypt the JWE
Use this snippet to decrypt the compact JWE string with your RSA private key:
#include <stdio.h> #include <stdlib.h> #include <cjose/cjose.h> int main() { cjose_err err; cjose_ctx *ctx = cjose_ctx_new(NULL, &err); if (!ctx) { fprintf(stderr, "Failed to create context: %s\n", err.message); return 1; } // Load your RSA private key (replace with your actual PEM string) const char *priv_key_pem = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQ...\n-----END PRIVATE KEY-----"; cjose_jwk_t *priv_key = cjose_jwk_from_pem(priv_key_pem, strlen(priv_key_pem), &err); if (!priv_key) { fprintf(stderr, "Failed to load private key: %s\n", err.message); cjose_ctx_free(ctx); return 1; } // JWE compact string from the encryption step const char *jwe_compact = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.Z6eD...AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.Mz-VPPyU4RlcuYv1IwIvzw"; // Parse the compact JWE string into a JWE object cjose_jwe_t *jwe = cjose_jwe_parse(ctx, jwe_compact, strlen(jwe_compact), &err); if (!jwe) { fprintf(stderr, "Failed to parse JWE: %s\n", err.message); cjose_jwk_free(priv_key); cjose_ctx_free(ctx); return 1; } // Decrypt to retrieve the original payload cjose_string_t *payload = cjose_jwe_decrypt(jwe, priv_key, &err); if (payload) { printf("Decrypted Payload:\n%s\n", payload->data); cjose_string_free(payload); } // Clean up resources cjose_jwe_free(jwe); cjose_jwk_free(priv_key); cjose_ctx_free(ctx); return 0; }
If you prefer the JSON serialization format (per RFC 7516), here’s a valid example (all values are Base64URL-encoded):
{
"protected": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ",
"encrypted_key": "Z6eD9cGJbF9lK1t5aUJ0aGJzU1RzWm9vbGJvbG9zZW1hbGxvd29ybGQ",
"iv": "AxY8DCtDaGlsbGljb3RoZQ",
"ciphertext": "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
"tag": "Mz-VPPyU4RlcuYv1IwIvzw"
}
To generate this format with cjose, replace CJOSE_SERIALIZE_COMPACT with CJOSE_SERIALIZE_JSON in the cjose_jwe_serialize function.
- Key Management: Never hardcode keys in your code. Use secure key stores (OS-level keychains, HSMs) to retrieve keys at runtime.
- Algorithm Selection: Stick to IETF-recommended algorithms for security:
- Asymmetric: RSA-OAEP-256 (more secure than basic RSA-OAEP)
- Symmetric: A256GCM (provides both confidentiality and integrity via authenticated encryption)
- Error Handling: Always check the
cjose_errobject after every cjose function call—this helps catch issues like invalid keys, malformed JWE, or unsupported algorithms. - Payload Size: JWE adds overhead, so avoid encrypting extremely large payloads. For big data, use a hybrid approach: encrypt the payload with a symmetric key, then encrypt that key with JWE.
内容的提问来源于stack exchange,提问作者samir

