Reputation: 13
I had a use case where I needed to create PDF signing functionality using a security key. For that I am using a Yubikey 5A, and its yubico-piv-tool library. I have put together some code using various resources, but still can't seem to get the end result.
void signPDF(const char* pdfFilePath, const char* yubikeyPIN) {
// Open PDF file
HPDF_Doc pdf = HPDF_New(error_handler, NULL);
if (!pdf) {
fprintf(stderr, "Error: Cannot create PDF object\n");
return;
}
// Add a page to the document
HPDF_Page page = HPDF_AddPage(pdf);
// Set the font and size
HPDF_Font font = HPDF_GetFont(pdf, "Helvetica", NULL);
HPDF_Page_SetFontAndSize(page, font, 24);
// Write some text
HPDF_Page_BeginText(page);
HPDF_Page_TextOut(page, 50, 700, "Not so Random!");
HPDF_Page_EndText(page);
// Save PDF content to stream
if (HPDF_OK != HPDF_SaveToStream(pdf)) {
fprintf(stderr, "Error: Cannot save PDF to stream\n");
HPDF_Free(pdf);
return;
}
// Get the size of the stream
HPDF_UINT32 contentLen = HPDF_GetStreamSize(pdf);
// Allocate memory for the content
HPDF_BYTE *content = (HPDF_BYTE *)malloc(contentLen);
// Perform signing operation using YubiKey's PIV
ykpiv_state *state;
if (ykpiv_init(&state, 0) != YKPIV_OK) {
fprintf(stderr, "Error: Cannot initialize YubiKey PIV\n");
return;
}
ykpiv_rc res = ykpiv_connect(state, NULL);
if (res != YKPIV_OK) {
fprintf(stderr, "Error: ykpiv_connect failed with code %d\n", res);
return;
}
int tries;
res = ykpiv_verify(state, yubikeyPIN, &tries);
if (res != YKPIV_OK) {
fprintf(stderr, "Error: Cannot authenticate with YubiKey\n");
return;
}
size_t signatureLen = YKPIV_MAX_SIGNATURE_SIZE;
unsigned char signature[YKPIV_MAX_SIGNATURE_SIZE];
res =
ykpiv_sign_data(state, content, contentLen, signature, &signatureLen,
YKPIV_ALGO_ECCP256, YKPIV_KEY_SIGNATURE);
if (res != YKPIV_OK) {
fprintf(stderr, "Error: Cannot sign PDF content. Error code: %d\n", res);
ykpiv_done(state);
HPDF_Free(pdf);
return;
}
// Save the signed PDF file
if (HPDF_SaveToFile(pdf, "signed_example.pdf") != HPDF_OK) {
fprintf(stderr, "Error: Cannot save signed PDF file.\n");
return;
}
printf("PDF file signed successfully.\n");
}void signPDF(const char* pdfFilePath, const char* yubikeyPIN) {
// Open PDF file
HPDF_Doc pdf = HPDF_New(error_handler, NULL);
if (!pdf) {
fprintf(stderr, "Error: Cannot create PDF object\n");
return;
}
// Add a page to the document
HPDF_Page page = HPDF_AddPage(pdf);
// Set the font and size
HPDF_Font font = HPDF_GetFont(pdf, "Helvetica", NULL);
HPDF_Page_SetFontAndSize(page, font, 24);
// Write some text
HPDF_Page_BeginText(page);
HPDF_Page_TextOut(page, 50, 700, "Not so Random!");
HPDF_Page_EndText(page);
// Save PDF content to stream
if (HPDF_OK != HPDF_SaveToStream(pdf)) {
fprintf(stderr, "Error: Cannot save PDF to stream\n");
HPDF_Free(pdf);
return;
}
// Get the size of the stream
HPDF_UINT32 contentLen = HPDF_GetStreamSize(pdf);
// Allocate memory for the content
HPDF_BYTE *content = (HPDF_BYTE *)malloc(contentLen);
// Perform signing operation using YubiKey's PIV
ykpiv_state *state;
if (ykpiv_init(&state, 0) != YKPIV_OK) {
fprintf(stderr, "Error: Cannot initialize YubiKey PIV\n");
return;
}
ykpiv_rc res = ykpiv_connect(state, NULL);
if (res != YKPIV_OK) {
fprintf(stderr, "Error: ykpiv_connect failed with code %d\n", res);
return;
}
int tries;
res = ykpiv_verify(state, yubikeyPIN, &tries);
if (res != YKPIV_OK) {
fprintf(stderr, "Error: Cannot authenticate with YubiKey\n");
return;
}
size_t signatureLen = YKPIV_MAX_SIGNATURE_SIZE;
unsigned char signature[YKPIV_MAX_SIGNATURE_SIZE];
res =
ykpiv_sign_data(state, content, contentLen, signature, &signatureLen,
YKPIV_ALGO_ECCP256, YKPIV_KEY_SIGNATURE);
if (res != YKPIV_OK) {
fprintf(stderr, "Error: Cannot sign PDF content. Error code: %d\n", res);
ykpiv_done(state);
HPDF_Free(pdf);
return;
}
// Save the signed PDF file
if (HPDF_SaveToFile(pdf, "signed_example.pdf") != HPDF_OK) {
fprintf(stderr, "Error: Cannot save signed PDF file.\n");
return;
}
printf("PDF file signed successfully.\n");
}
Basically what the code does is that it create a document with a single page ( this is not really relevant, as when I will figure out the signing, I can load the PDF document straight from storage), and signs its content using the private key in the Yubikey. But my problem is, I can't figure out how to append that signed content at the correct byte ranges in the PDF.
I have tried using PoDoFo library, but it seems to need to work only with the private key available beforehand, which defeats the entire purpose because the key should not be taken out of the YubiKey anyway. I have tried getting around how PDFs are signed, but creating the functionality from scratch for this specific use case seems to be too complex.
What can I try next? The language is not a problem, if the solution can be implemented.
Upvotes: 1
Views: 170