FILE * smime_encrypt(FILE *ip, const char *ignored, const char *to) { NSSCMSMessage *msg; NSSCMSContentInfo *content; NSSCMSEnvelopedData *data; NSSCMSRecipientInfo *info; CERTCertificate *cert[2]; CERTCertDBHandle *handle; SECOidTag tag; FILE *hp, *pp, *yp; int keysize; char *nickname, *vn; int vs; if (nss_init() != OKAY) return NULL; handle = CERT_GetDefaultCertDB(); vn = ac_alloc(vs = strlen(to) + 30); snprintf(vn, vs, "smime-nickname-%s", to); nickname = value(vn); ac_free(vn); if ((cert[0] = CERT_FindCertByNicknameOrEmailAddr(handle, nickname ? nickname : (char *)to)) == NULL) { if (nickname) fprintf(stderr, "Cannot find certificate \"%s\".\n", nickname); else fprintf(stderr, "Cannot find certificate for <%s>.\n", to); return NULL; } cert[1] = NULL; if (getcipher(to, &tag, &keysize) != OKAY) return NULL; if ((msg = NSS_CMSMessage_Create(NULL)) == NULL) { fprintf(stderr, "Cannot create CMS message.\n"); return NULL; } if ((data = NSS_CMSEnvelopedData_Create(msg, tag, keysize)) == NULL) { fprintf(stderr, "Cannot create enveloped data.\n"); return NULL; } content = NSS_CMSMessage_GetContentInfo(msg); if (NSS_CMSContentInfo_SetContent_EnvelopedData(msg, content, data) != SECSuccess) { fprintf(stderr, "Cannot attach enveloped data.\n"); return NULL; } content = NSS_CMSEnvelopedData_GetContentInfo(data); if (NSS_CMSContentInfo_SetContent_Data(msg, content, NULL, PR_FALSE) != SECSuccess) { fprintf(stderr, "Cannot attach CMS data.\n"); return NULL; } if ((info = NSS_CMSRecipientInfo_Create(msg, cert[0])) == NULL) { fprintf(stderr, "Cannot create CMS recipient information.\n"); return NULL; } if (NSS_CMSEnvelopedData_AddRecipient(data, info) != SECSuccess) { fprintf(stderr, "Cannot add CMS recipient information.\n"); return NULL; } CERT_DestroyCertificate(cert[0]); if ((yp = encode(ip, &hp, &pp, msg, base64_cb)) == NULL) return NULL; NSS_CMSMessage_Destroy(msg); return smime_encrypt_assemble(hp, yp); }
static int sm_encrypt(CamelCipherContext *context, const char *userid, GPtrArray *recipients, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) { struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv; /*NSSCMSRecipientInfo **recipient_infos;*/ CERTCertificate **recipient_certs = NULL; NSSCMSContentInfo *cinfo; PK11SymKey *bulkkey = NULL; SECOidTag bulkalgtag; int bulkkeysize, i; CK_MECHANISM_TYPE type; PK11SlotInfo *slot; PLArenaPool *poolp; NSSCMSMessage *cmsg = NULL; NSSCMSEnvelopedData *envd; NSSCMSEncoderContext *enc = NULL; CamelStreamMem *mem; CamelStream *ostream = NULL; CamelDataWrapper *dw; CamelContentType *ct; poolp = PORT_NewArena(1024); if (poolp == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM)); return -1; } /* Lookup all recipients certs, for later working */ recipient_certs = (CERTCertificate **)PORT_ArenaZAlloc(poolp, sizeof(*recipient_certs[0])*(recipients->len + 1)); if (recipient_certs == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM)); goto fail; } for (i=0;i<recipients->len;i++) { recipient_certs[i] = CERT_FindCertByNicknameOrEmailAddr(p->certdb, recipients->pdata[i]); if (recipient_certs[i] == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find certificate for `%s'"), recipients->pdata[i]); goto fail; } } /* Find a common algorithm, probably 3DES anyway ... */ if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipient_certs, &bulkalgtag, &bulkkeysize) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find common bulk encryption algorithm")); goto fail; } /* Generate a new bulk key based on the common algorithm - expensive */ type = PK11_AlgtagToMechanism(bulkalgtag); slot = PK11_GetBestSlot(type, context); if (slot == NULL) { /* PORT_GetError(); ?? */ camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot allocate slot for encryption bulk key")); goto fail; } bulkkey = PK11_KeyGen(slot, type, NULL, bulkkeysize/8, context); PK11_FreeSlot(slot); /* Now we can start building the message */ /* msg->envelopedData->data */ cmsg = NSS_CMSMessage_Create(NULL); if (cmsg == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Message")); goto fail; } envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, bulkkeysize); if (envd == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Enveloped data")); goto fail; } cinfo = NSS_CMSMessage_GetContentInfo(cmsg); if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS Enveloped data")); goto fail; } cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS data object")); goto fail; } /* add recipient certs */ for (i=0;recipient_certs[i];i++) { NSSCMSRecipientInfo *ri = NSS_CMSRecipientInfo_Create(cmsg, recipient_certs[i]); if (ri == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Recipient information")); goto fail; } if (NSS_CMSEnvelopedData_AddRecipient(envd, ri) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add CMS Recipient information")); goto fail; } } /* dump it out */ ostream = camel_stream_mem_new(); enc = NSS_CMSEncoder_Start(cmsg, sm_write_stream, ostream, NULL, NULL, NULL, NULL, sm_decrypt_key, bulkkey, NULL, NULL); if (enc == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create encoder context")); goto fail; } /* FIXME: Stream the input */ /* FIXME: Canonicalise the input? */ mem = (CamelStreamMem *)camel_stream_mem_new(); camel_data_wrapper_write_to_stream((CamelDataWrapper *)ipart, (CamelStream *)mem); if (NSS_CMSEncoder_Update(enc, (char *) mem->buffer->data, mem->buffer->len) != SECSuccess) { NSS_CMSEncoder_Cancel(enc); camel_object_unref(mem); camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to add data to encoder")); goto fail; } camel_object_unref(mem); if (NSS_CMSEncoder_Finish(enc) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to encode data")); goto fail; } PK11_FreeSymKey(bulkkey); NSS_CMSMessage_Destroy(cmsg); for (i=0;recipient_certs[i];i++) CERT_DestroyCertificate(recipient_certs[i]); PORT_FreeArena(poolp, PR_FALSE); dw = camel_data_wrapper_new(); camel_data_wrapper_construct_from_stream(dw, ostream); camel_object_unref(ostream); dw->encoding = CAMEL_TRANSFER_ENCODING_BINARY; ct = camel_content_type_new("application", "x-pkcs7-mime"); camel_content_type_set_param(ct, "name", "smime.p7m"); camel_content_type_set_param(ct, "smime-type", "enveloped-data"); camel_data_wrapper_set_mime_type_field(dw, ct); camel_content_type_unref(ct); camel_medium_set_content_object((CamelMedium *)opart, dw); camel_object_unref(dw); camel_mime_part_set_disposition(opart, "attachment"); camel_mime_part_set_filename(opart, "smime.p7m"); camel_mime_part_set_description(opart, "S/MIME Encrypted Message"); camel_mime_part_set_encoding(opart, CAMEL_TRANSFER_ENCODING_BASE64); return 0; fail: if (ostream) camel_object_unref(ostream); if (cmsg) NSS_CMSMessage_Destroy(cmsg); if (bulkkey) PK11_FreeSymKey(bulkkey); if (recipient_certs) { for (i=0;recipient_certs[i];i++) CERT_DestroyCertificate(recipient_certs[i]); } PORT_FreeArena(poolp, PR_FALSE); return -1; }
// nsCMSSecureMessage::SendMessage nsresult nsCMSSecureMessage:: SendMessage(const char *msg, const char *base64Cert, char ** _retval) { nsNSSShutDownPreventionLock locker; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage\n")); nsresult rv = NS_OK; CERTCertificate *cert = 0; NSSCMSMessage *cmsMsg = 0; unsigned char *certDER = 0; PRInt32 derLen; NSSCMSEnvelopedData *env; NSSCMSContentInfo *cinfo; NSSCMSRecipientInfo *rcpt; SECItem item; SECItem output; PLArenaPool *arena = PORT_NewArena(1024); SECStatus s; nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); /* Step 0. Create a CMS Message */ cmsMsg = NSS_CMSMessage_Create(NULL); if (!cmsMsg) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create NSSCMSMessage\n")); rv = NS_ERROR_FAILURE; goto done; } /* Step 1. Import the certificate into NSS */ rv = decode(base64Cert, &certDER, &derLen); if (NS_FAILED(rv)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode / import cert into NSS\n")); goto done; } cert = CERT_DecodeCertFromPackage((char *)certDER, derLen); if (!cert) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode cert from package\n")); rv = NS_ERROR_FAILURE; goto done; } #if 0 cert->dbhandle = CERT_GetDefaultCertDB(); /* work-around */ #endif /* Step 2. Get a signature cert */ /* Step 3. Build inner (signature) content */ /* Step 4. Build outer (enveloped) content */ env = NSS_CMSEnvelopedData_Create(cmsMsg, SEC_OID_DES_EDE3_CBC, 0); if (!env) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create envelope data\n")); rv = NS_ERROR_FAILURE; goto done; } cinfo = NSS_CMSEnvelopedData_GetContentInfo(env); item.data = (unsigned char *)msg; item.len = strlen(msg); /* XPCOM equiv?? */ s = NSS_CMSContentInfo_SetContent_Data(cmsMsg, cinfo, 0, PR_FALSE); if (s != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content data\n")); rv = NS_ERROR_FAILURE; goto done; } rcpt = NSS_CMSRecipientInfo_Create(cmsMsg, cert); if (!rcpt) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create recipient info\n")); rv = NS_ERROR_FAILURE; goto done; } s = NSS_CMSEnvelopedData_AddRecipient(env, rcpt); if (s != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't add recipient\n")); rv = NS_ERROR_FAILURE; goto done; } /* Step 5. Add content to message */ cinfo = NSS_CMSMessage_GetContentInfo(cmsMsg); s = NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMsg, cinfo, env); if (s != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content enveloped data\n")); rv = NS_ERROR_FAILURE; goto done; } /* Step 6. Encode */ NSSCMSEncoderContext *ecx; output.data = 0; output.len = 0; ecx = NSS_CMSEncoder_Start(cmsMsg, 0, 0, &output, arena, 0, ctx, 0, 0, 0, 0); if (!ecx) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't start cms encoder\n")); rv = NS_ERROR_FAILURE; goto done; } s = NSS_CMSEncoder_Update(ecx, msg, strlen(msg)); if (s != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't update encoder\n")); rv = NS_ERROR_FAILURE; goto done; } s = NSS_CMSEncoder_Finish(ecx); if (s != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't finish encoder\n")); rv = NS_ERROR_FAILURE; goto done; } /* Step 7. Base64 encode and return the result */ rv = encode(output.data, output.len, _retval); done: if (certDER) nsCRT::free((char *)certDER); if (cert) CERT_DestroyCertificate(cert); if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); if (arena) PORT_FreeArena(arena, PR_FALSE); /* PR_FALSE? */ return rv; }
NS_IMETHODIMP nsCMSMessage::CreateEncrypted(nsIArray * aRecipientCerts) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted\n")); NSSCMSContentInfo *cinfo; NSSCMSEnvelopedData *envd; NSSCMSRecipientInfo *recipientInfo; nsZeroTerminatedCertArray recipientCerts; SECOidTag bulkAlgTag; int keySize; PRUint32 i; nsCOMPtr<nsIX509Cert2> nssRecipientCert; nsresult rv = NS_ERROR_FAILURE; // Check the recipient certificates // PRUint32 recipientCertCount; aRecipientCerts->GetLength(&recipientCertCount); PR_ASSERT(recipientCertCount > 0); if (!recipientCerts.allocate(recipientCertCount)) { goto loser; } for (i=0; i<recipientCertCount; i++) { nsCOMPtr<nsIX509Cert> x509cert = do_QueryElementAt(aRecipientCerts, i); nssRecipientCert = do_QueryInterface(x509cert); if (!nssRecipientCert) return NS_ERROR_FAILURE; CERTCertificate *c = nssRecipientCert->GetCert(); CERTCertificateCleaner rcCleaner(c); recipientCerts.set(i, c); } // Find a bulk key algorithm // if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientCerts.getRawArray(), &bulkAlgTag, &keySize) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't find bulk alg for recipients\n")); rv = NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG; goto loser; } m_cmsMsg = NSS_CMSMessage_Create(NULL); if (m_cmsMsg == nsnull) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create new cms message\n")); rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } if ((envd = NSS_CMSEnvelopedData_Create(m_cmsMsg, bulkAlgTag, keySize)) == nsnull) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create enveloped data\n")); goto loser; } cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); if (NSS_CMSContentInfo_SetContent_EnvelopedData(m_cmsMsg, cinfo, envd) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create content enveloped data\n")); goto loser; } cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nsnull, PR_FALSE) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't set content data\n")); goto loser; } // Create and attach recipient information // for (i=0; i < recipientCertCount; i++) { CERTCertificate *rc = recipientCerts.get(i); CERTCertificateCleaner rcCleaner(rc); if ((recipientInfo = NSS_CMSRecipientInfo_Create(m_cmsMsg, rc)) == nsnull) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create recipient info\n")); goto loser; } if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientInfo) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't add recipient info\n")); goto loser; } } return NS_OK; loser: if (m_cmsMsg) { NSS_CMSMessage_Destroy(m_cmsMsg); m_cmsMsg = nsnull; } return rv; }
UtlBoolean SmimeBody::nssSmimeEncrypt(int numResipientCerts, const char* derPublicKeyCerts[], int derPublicKeyCertLengths[], const char* dataToEncrypt, int dataToEncryptLength, UtlBoolean encryptedDataInBase64Format, UtlString& encryptedData) { UtlBoolean encryptionSucceeded = FALSE; encryptedData.remove(0); #ifdef ENABLE_NSS_SMIME // nickname can be NULL as we are not putting this in a database char *nickname = NULL; // copyDER = true so we copy the DER format cert passed in so memory // is internally alloc'd and freed PRBool copyDER = true; // Create an envelope or container for the encrypted data SECOidTag algorithm = SEC_OID_DES_EDE3_CBC; // or SEC_OID_AES_128_CBC // Should be able to get the key size from the cert somehow int keysize = 1024; NSSCMSMessage* cmsMessage = NSS_CMSMessage_Create(NULL); NSSCMSEnvelopedData* myEnvelope = NSS_CMSEnvelopedData_Create(cmsMessage, algorithm, keysize); // Do the following for each recipient if there is more than one. // For each recipient: for(int certIndex = 0; certIndex < numResipientCerts; certIndex++) { // Convert the DER to a NSS CERT SECItem derFormatCertItem; SECItem* derFormatCertItemPtr = &derFormatCertItem; derFormatCertItem.data = (unsigned char*) derPublicKeyCerts[certIndex]; derFormatCertItem.len = derPublicKeyCertLengths[certIndex]; CERTCertificate* myCertFromDer = NULL; myCertFromDer = __CERT_DecodeDERCertificate(&derFormatCertItem, copyDER, nickname); // Add just the recipient Subject key Id, if it exists to the envelope // This is the minimal information needed to identify which recipient // the the symetric/session key is encrypted for NSSCMSRecipientInfo* recipientInfo = NULL; // Add the full set of recipient information including // the Cert. issuer location and org. info. recipientInfo = NSS_CMSRecipientInfo_Create(cmsMessage, myCertFromDer); if(recipientInfo) { if(NSS_CMSEnvelopedData_AddRecipient(myEnvelope , recipientInfo) != SECSuccess) { NSS_CMSEnvelopedData_Destroy(myEnvelope); myEnvelope = NULL; NSS_CMSRecipientInfo_Destroy(recipientInfo); } } // No recipientInfo else { NSS_CMSEnvelopedData_Destroy(myEnvelope); myEnvelope = NULL; } } //end for each recipient // Get the content out of the envelop NSSCMSContentInfo* envelopContentInfo = NSS_CMSEnvelopedData_GetContentInfo(myEnvelope); //TODO: why are we copying or setting the content pointer from the envelope into the msg???????? if (NSS_CMSContentInfo_SetContent_Data(cmsMessage, envelopContentInfo, NULL, PR_FALSE) != SECSuccess) { // release cmsg and other stuff NSS_CMSEnvelopedData_Destroy(myEnvelope); myEnvelope = NULL; } //TODO: why are we copying or setting the content pointer from the message and // putting it back into the msg???????? NSSCMSContentInfo* messageContentInfo = NSS_CMSMessage_GetContentInfo(cmsMessage); if(NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMessage, messageContentInfo, myEnvelope) != SECSuccess) { // release cmsg and other stuff NSS_CMSEnvelopedData_Destroy(myEnvelope); myEnvelope = NULL; } if(cmsMessage) { // Create an encoder and context to do the encryption. // The encodedItem will stort the encoded result //SECItem encodedItem; //encodedItem.data = NULL; //encodedItem.len = 0; //SECITEM_AllocItem(NULL, &encodedItem, 0); printf("start encoder\n"); NSSCMSEncoderContext* encoderContext = NSS_CMSEncoder_Start(cmsMessage, nssOutToUtlString, &encryptedData, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); // Add encrypted content printf("update encoder\n"); NSS_CMSEncoder_Update(encoderContext, dataToEncrypt, dataToEncryptLength); // Finished encrypting printf("finish encoder\n"); NSS_CMSEncoder_Finish(encoderContext); myEnvelope = NULL; if(encryptedData.length() > 0) { encryptionSucceeded = TRUE; } // Clean up the message memory, the envelop gets cleaned up // with the message NSS_CMSMessage_Destroy(cmsMessage); cmsMessage = NULL; } #else Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SmimeBody::nssSmimeEncrypt invoked with ENABLE_NSS_SMIME not defined"); #endif return(encryptionSucceeded); }