/* $Id: XmlEnc.c $ */ /* $Date: 2018-10-29 09:29:00 $ $Revision: 1.0.1 $ */ /******************************* LICENSE *********************************** * Copyright (C) 2018 David Ireland, DI Management Services Pty Limited. * All rights reserved. <https://di-mgt.com.au> <https://cryptosys.net> * The code in this module is licensed under the terms of the MIT license. * For a copy, see <http://opensource.org/licenses/MIT> **************************************************************************** */ /* THIS CODE DEMONSTRATES: * Using C programming language with CryptoSys PKI. * Reading private and public RSA keys from PXF/P12 data in memory. * Extracting X509 certificate from PFX data in memory. * Encrypting and decrypting using RSA-OAEP with SHA-256 digest and MGF options. * Base64 encoding and decoding. */ /* Link to `diCrPKI.dll` in system directory using .lib file in current directory: Properties > Linker > Input > AdditionalDependencies=.\diCrPKI.lib;%(AdditionalDependencies) */ #if _MSC_VER >= 1100 /* Detect memory leaks in MSVC++ */ #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #else #include <stdlib.h> #endif #ifdef NDEBUG /* Make sure assertion testing is turned on */ #undef NDEBUG #endif #include <assert.h> #include <stdio.h> #include <string.h> #include "diCrPKI.h" /* typedef for BYTE type (uint8_t) */ typedef unsigned char BYTE; /* INTERNAL UTILITIES */ /** Reads a binary file into a byte array * @return Pointer to allocated byte array or NULL on error * @remark Sets `*plen` with length of byte array * @remark Array is an exact copy of file bytes with _no_ terminating zero * @remark User must free allocated memory */ static BYTE *read_a_binary_file(const char *fname, long *plen) { FILE *fp; BYTE *buf; long flen; size_t nread; fp = fopen(fname, "rb"); assert(fp != NULL); fseek(fp, 0, SEEK_END); flen = ftell(fp); assert(flen >= 0); buf = malloc(flen); rewind(fp); nread = fread(buf, 1, flen, fp); fclose(fp); *plen = (long)nread; return buf; } int main(void) { const char *pfxname = "bob.pfx"; // Encrypted password = "password" const char *password = "password"; // high security!! const char *certname = "bob.cer"; // To be created // string values we will use below const char *begin_pem = "-----BEGIN PKCS12-----"; const char *end_pem = "-----END PKCS12-----"; BYTE *lpPfxData, *lpEncKey, *lpCT, *lpDecKey; char *tmp, *pfxstr; char *prikey, *pubkey; char *ciphervalue, *hexkey; long flen, nchars, slen, nbytes, clen, klen, dklen; // Our session key - we should generate at random each time, but for demo we use a fixed value BYTE sessionKey[] = { 0x56, 0xBE, 0xEC, 0x5B, 0x9E, 0x40, 0xC9, 0xE1, 0xBC, 0x42, 0xDE, 0x9C, 0x1F, 0x1A, 0x3D, 0x2F, 0xF3, 0x87, 0xBE, 0xB0, 0x89, 0xB5, 0xDC, 0xA9, 0xF0, 0x41, 0x79, 0xA6, 0x58, 0x36, 0x6B, 0xE2, }; /* MSVC memory leak checking stuff */ #if _MSC_VER >= 1100 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif printf("PKI version=%ld\n", PKI_Version(NULL, NULL)); // Require at least PKI version 12.0 assert(PKI_Version(NULL, NULL) >= 120000); /* Read in P12/PFX file to memory - in practice this would already exist */ lpPfxData = read_a_binary_file(pfxname, &flen); assert(lpPfxData); printf("FILE: %s %ld bytes\n", pfxname, flen); /* This data is in binary form - first byte is 0x30 If it's already in PEM format (begins with "-----BEGIN"), you can omit this step */ // Convert to base64 and add PEM encapsulation (messy in pure C!) nchars = CNV_B64StrFromBytes(NULL, 0, lpPfxData, flen); assert(nchars > 0); tmp = calloc(1, nchars + 1); // NB +1 nchars = CNV_B64StrFromBytes(tmp, nchars, lpPfxData, flen); slen = nchars + strlen(begin_pem) + strlen(end_pem) + 1; // NB +1 here pfxstr = calloc(1, slen); // slen is size of buffer incl term zero strcpy_s(pfxstr, slen, begin_pem); strcat_s(pfxstr, slen, tmp); strcat_s(pfxstr, slen, end_pem); printf("%s\n", pfxstr); /* Note that line breaks are not required in the PEM string, and we ignore the word "PKCS12" (other apps may not) We will just look for the strings "-----BEGIN" and "-----END" (exactly 5 dash characters in both cases) */ /* Now we have PFX in PEM format in a string, we can use it directly as a parameter in our functions */ nchars = RSA_ReadAnyPrivateKey(NULL, 0, pfxstr, password, 0); assert(nchars > 0); prikey = malloc(nchars + 1); nchars = RSA_ReadAnyPrivateKey(prikey, nchars, pfxstr, password, 0); assert(nchars > 0); // quick debug printf("Private Key = %ld bits\n", RSA_KeyBits(prikey)); /* We can get the public key (it's already included in the private key) */ nchars = RSA_PublicKeyFromPrivate(NULL, 0, prikey, 0); assert(nchars > 0); pubkey = malloc(nchars + 1); nchars = RSA_PublicKeyFromPrivate(pubkey, nchars, prikey, 0); assert(nchars > 0); printf("Public Key = %ld bits\n", RSA_KeyBits(pubkey)); /* And we can export the .CER file from inside the PFX data */ nbytes = X509_GetCertFromPFX(certname, pfxstr, password, 0); assert(nbytes > 0); printf("Written %ld bytes to '%s'\n", nbytes, certname); /* Now lets encrypt our session key using RSA-OAEP - see manual for details about options https://cryptosys.net/pki/manpki/pki_RSA_Encrypt.html We use the public key to encrypt <xenc:EncryptionMethod Algorithm="http://www.w3.org/2009/xmlenc11#rsa-oaep"> <xenc11:MGF Algorithm="http://www.w3.org/2009/xmlenc11#mgf1sha256"/> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> </xenc:EncryptionMethod> PKI_EME_OAEP => use rsa-oaep PKI_HASH_SHA256 => use sha256 for the DigestAlgorithm AND mgf1sha256 for MGF Algorithm */ klen = sizeof(sessionKey); // 256 bits/8 = 32 bytes nbytes = RSA_Encrypt(NULL, 0, sessionKey, klen, pubkey, "", PKI_EME_OAEP | PKI_HASH_SHA256); assert(nbytes > 0); // We must encrypt to an array of bytes first lpEncKey = malloc(nbytes); // NB no extra here, it's a bytes array nbytes = RSA_Encrypt(lpEncKey, nbytes, sessionKey, klen, pubkey, "", PKI_EME_OAEP | PKI_HASH_SHA256); assert(nbytes > 0); /* Finally encode in base64 */ nchars = CNV_B64StrFromBytes(NULL, 0, lpEncKey, nbytes); assert(nchars > 0); ciphervalue = malloc(nchars + 1); nchars = CNV_B64StrFromBytes(ciphervalue, nchars, lpEncKey, nbytes); /* Output as part of XML document - NB this value will be different each time */ printf("<CipherValue>%s</CipherValue>\n", ciphervalue); /* PART 2 */ /* We have (1) the CipherValue in a base64-encoded string (extracted from the XML) (2) the private key (extracted from the PFX above) - Let's decrypt */ /* Decode the base64 to a byte array (NB just base64, no XML markup here) */ clen = CNV_BytesFromB64Str(NULL, 0, ciphervalue); assert(clen > 0); lpCT = malloc(clen); clen = CNV_BytesFromB64Str(lpCT, clen, ciphervalue); assert(clen > 0); /* Decrypt the byte array - you need to specify the parameters explicitly detected from the XML by other means */ dklen = RSA_Decrypt(NULL, 0, lpCT, clen, prikey, "", "", PKI_EME_OAEP | PKI_HASH_SHA256); assert(dklen > 0); lpDecKey = calloc(1, dklen); dklen = RSA_Decrypt(lpDecKey, dklen, lpCT, clen, prikey, "", "", PKI_EME_OAEP | PKI_HASH_SHA256); assert(nbytes > 0); /* Display decrypted session key in hex */ nchars = CNV_HexStrFromBytes(NULL, 0, lpDecKey, dklen); assert(nchars > 0); hexkey = malloc(nchars + 1); nchars = CNV_HexStrFromBytes(hexkey, nchars, lpDecKey, dklen); printf("SESSION KEY: 0x%s\n", hexkey); /* Free up memory*/ free(lpPfxData); free(tmp); free(pfxstr); free(prikey); free(pubkey); free(lpEncKey); free(ciphervalue); free(lpCT); free(lpDecKey); free(hexkey); printf("\nALL DONE.\n"); return 0; } /* EXPECTED FORM OF OUTPUT: PKI version=120000 FILE: bob.pfx 1672 bytes -----BEGIN PKCS12----- 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 CKQ68TJy9enBAgIIAA== -----END PKCS12----- Private Key = 1024 bits Public Key = 1024 bits Written 555 bytes to 'bob.cer' <CipherValue>UyFrzuANad1RDSDJjZk4PJCUeh35CcPpFGhFjw90S3YY8HE3ZiIAZ82kyGtfSh+0ET +EBtP01nzdzQulm/ZmqXDHk+0RWR3EeDy0z9wJbg2atHvaChjfPFXikBC1IZ1ZcWgT4GZlJt27bVWzm UW9fnOH5Ke45AbZi4EVtpKgyuU=</CipherValue> SESSION KEY: 0x56BEEC5B9E40C9E1BC42DE9C1F1A3D2FF387BEB089B5DCA9F04179A658366BE2 ALL DONE. */