smimeutil.c
来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 715 行 · 第 1/2 页
C
715 行
* smime_choose_cipher - choose a cipher that works for all the recipients * * "scert" - sender's certificate * "rcerts" - recipient's certificates */static longsmime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts){ PRArenaPool *poolp; long cipher; long chosen_cipher; int *cipher_abilities; int *cipher_votes; int weak_mapi; int strong_mapi; int rcount, mapi, max, i; PRBool scert_is_fortezza = (scert == NULL) ? PR_FALSE : PK11_FortezzaHasKEA(scert); chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */ weak_mapi = smime_mapi_by_cipher(chosen_cipher); poolp = PORT_NewArena (1024); /* XXX what is right value? */ if (poolp == NULL) goto done; cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int)); cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int)); if (cipher_votes == NULL || cipher_abilities == NULL) goto done; /* If the user has the Fortezza preference turned on, make * that the strong cipher. Otherwise, use triple-DES. */ strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168); if (scert_is_fortezza) { mapi = smime_mapi_by_cipher(SMIME_FORTEZZA); if (mapi >= 0 && smime_cipher_map[mapi].enabled) strong_mapi = mapi; } /* walk all the recipient's certs */ for (rcount = 0; rcerts[rcount] != NULL; rcount++) { SECItem *profile; NSSSMIMECapability **caps; int pref; /* the first cipher that matches in the user's SMIME profile gets * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1 * and so on. If every cipher matches, the last one gets 1 (one) vote */ pref = smime_cipher_map_count; /* find recipient's SMIME profile */ profile = CERT_FindSMimeProfile(rcerts[rcount]); if (profile != NULL && profile->data != NULL && profile->len > 0) { /* we have a profile (still DER-encoded) */ caps = NULL; /* decode it */ if (SEC_ASN1DecodeItem(poolp, &caps, NSSSMIMECapabilitiesTemplate, profile) == SECSuccess && caps != NULL) { /* walk the SMIME capabilities for this recipient */ for (i = 0; caps[i] != NULL; i++) { cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]); mapi = smime_mapi_by_cipher(cipher); if (mapi >= 0) { /* found the cipher */ cipher_abilities[mapi]++; cipher_votes[mapi] += pref; --pref; } } } } else { /* no profile found - so we can only assume that the user can do * the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */ SECKEYPublicKey *key; unsigned int pklen_bits; /* * if recipient's public key length is > 512, vote for a strong cipher * please not that the side effect of this is that if only one recipient * has an export-level public key, the strong cipher is disabled. * * XXX This is probably only good for RSA keys. What I would * really like is a function to just say; Is the public key in * this cert an export-length key? Then I would not have to * know things like the value 512, or the kind of key, or what * a subjectPublicKeyInfo is, etc. */ key = CERT_ExtractPublicKey(rcerts[rcount]); pklen_bits = 0; if (key != NULL) { pklen_bits = SECKEY_PublicKeyStrength (key) * 8; SECKEY_DestroyPublicKey (key); } if (pklen_bits > 512) { /* cast votes for the strong algorithm */ cipher_abilities[strong_mapi]++; cipher_votes[strong_mapi] += pref; pref--; } /* always cast (possibly less) votes for the weak algorithm */ cipher_abilities[weak_mapi]++; cipher_votes[weak_mapi] += pref; } if (profile != NULL) SECITEM_FreeItem(profile, PR_TRUE); } /* find cipher that is agreeable by all recipients and that has the most votes */ max = 0; for (mapi = 0; mapi < smime_cipher_map_count; mapi++) { /* if not all of the recipients can do this, forget it */ if (cipher_abilities[mapi] != rcount) continue; /* if cipher is not enabled or not allowed by policy, forget it */ if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed) continue; /* if we're not doing fortezza, but the cipher is fortezza, forget it */ if (!scert_is_fortezza && (smime_cipher_map[mapi].cipher == SMIME_FORTEZZA)) continue; /* now see if this one has more votes than the last best one */ if (cipher_votes[mapi] >= max) { /* if equal number of votes, prefer the ones further down in the list */ /* with the expectation that these are higher rated ciphers */ chosen_cipher = smime_cipher_map[mapi].cipher; max = cipher_votes[mapi]; } } /* if no common cipher was found, chosen_cipher stays at the default */done: if (poolp != NULL) PORT_FreeArena (poolp, PR_FALSE); return chosen_cipher;}/* * XXX This is a hack for now to satisfy our current interface. * Eventually, with more parameters needing to be specified, just * looking up the keysize is not going to be sufficient. */static intsmime_keysize_by_cipher (unsigned long which){ int keysize; switch (which) { case SMIME_RC2_CBC_40: keysize = 40; break; case SMIME_RC2_CBC_64: keysize = 64; break; case SMIME_RC2_CBC_128: keysize = 128; break; case SMIME_DES_CBC_56: case SMIME_DES_EDE3_168: case SMIME_FORTEZZA: /* * These are special; since the key size is fixed, we actually * want to *avoid* specifying a key size. */ keysize = 0; break; default: keysize = -1; break; } return keysize;}/* * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients * * it would be great for UI purposes if there would be a way to find out which recipients * prevented a strong cipher from being used... */SECStatusNSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize){ unsigned long cipher; int mapi; cipher = smime_choose_cipher(NULL, rcerts); mapi = smime_mapi_by_cipher(cipher); *bulkalgtag = smime_cipher_map[mapi].algtag; *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].algtag); return SECSuccess;}/* * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS * * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant * S/MIME capabilities attribute value. * * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include * symmetric ciphers, NO signature algorithms or key encipherment algorithms. * * "poolp" - arena pool to create the S/MIME capabilities data on * "dest" - SECItem to put the data in * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included */SECStatusNSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers){ NSSSMIMECapability *cap; NSSSMIMECapability **smime_capabilities; smime_cipher_map_entry *map; SECOidData *oiddata; SECItem *dummy; int i, capIndex; /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */ /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */ smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1) * sizeof(NSSSMIMECapability *)); if (smime_capabilities == NULL) return SECFailure; capIndex = 0; /* Add all the symmetric ciphers * We walk the cipher list backwards, as it is ordered by increasing strength, * we prefer the stronger cipher over a weaker one, and we have to list the * preferred algorithm first */ for (i = smime_cipher_map_count - 1; i >= 0; i--) { /* Find the corresponding entry in the cipher map. */ map = &(smime_cipher_map[i]); if (!map->enabled) continue; /* If we're using a non-Fortezza cert, only advertise non-Fortezza capabilities. (We advertise all capabilities if we have a Fortezza cert.) */ if ((!includeFortezzaCiphers) && (map->cipher == SMIME_FORTEZZA)) continue; /* get next SMIME capability */ cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability)); if (cap == NULL) break; smime_capabilities[capIndex++] = cap; oiddata = SECOID_FindOIDByTag(map->algtag); if (oiddata == NULL) break; cap->capabilityID.data = oiddata->oid.data; cap->capabilityID.len = oiddata->oid.len; cap->parameters.data = map->parms ? map->parms->data : NULL; cap->parameters.len = map->parms ? map->parms->len : 0; cap->cipher = smime_cipher_map[i].cipher; } /* XXX add signature algorithms */ /* XXX add key encipherment algorithms */ smime_capabilities[capIndex] = NULL; /* last one - now encode */ dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate); /* now that we have the proper encoded SMIMECapabilities (or not), * free the work data */ for (i = 0; smime_capabilities[i] != NULL; i++) PORT_Free(smime_capabilities[i]); PORT_Free(smime_capabilities); return (dummy == NULL) ? SECFailure : SECSuccess;}/* * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value * * "poolp" - arena pool to create the attr value on * "dest" - SECItem to put the data in * "cert" - certificate that should be marked as preferred encryption key * cert is expected to have been verified for EmailRecipient usage. */SECStatusNSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert){ NSSSMIMEEncryptionKeyPreference ekp; SECItem *dummy = NULL; PLArenaPool *tmppoolp; if (cert == NULL) goto loser; tmppoolp = PORT_NewArena(1024); if (tmppoolp == NULL) goto loser; /* XXX hardcoded IssuerSN choice for now */ ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN; ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert); if (ekp.id.issuerAndSN == NULL) goto loser; dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);loser: if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE); return (dummy == NULL) ? SECFailure : SECSuccess;}/* * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference - * find cert marked by EncryptionKeyPreference attribute * * "certdb" - handle for the cert database to look in * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute * * if certificate is supposed to be found among the message's included certificates, * they are assumed to have been imported already. */CERTCertificate *NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp){ PLArenaPool *tmppoolp = NULL; CERTCertificate *cert = NULL; NSSSMIMEEncryptionKeyPreference ekp; tmppoolp = PORT_NewArena(1024); if (tmppoolp == NULL) return NULL; /* decode DERekp */ if (SEC_ASN1DecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template, DERekp) != SECSuccess) goto loser; /* find cert */ switch (ekp.selector) { case NSSSMIMEEncryptionKeyPref_IssuerSN: cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN); break; case NSSSMIMEEncryptionKeyPref_RKeyID: case NSSSMIMEEncryptionKeyPref_SubjectKeyID: /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */ break; default: PORT_Assert(0); }loser: if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE); return cert;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?