static void fill_RSA_public_key(struct RSA_public_key *rsa, SECKEYPublicKey *pubkey) { passert(SECKEY_GetPublicKeyType(pubkey) == rsaKey); rsa->e = clone_secitem_as_chunk(pubkey->u.rsa.publicExponent, "e"); rsa->n = clone_secitem_as_chunk(pubkey->u.rsa.modulus, "n"); form_keyid(rsa->e, rsa->n, rsa->keyid, &rsa->k); }
OSStatus SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp, SecPublicKeyRef publickey, SecSymmetricKeyRef bulkkey, SecAsn1Item * encKey) { OSStatus rv; size_t data_len; //KeyType keyType; void *mark = NULL; mark = PORT_ArenaMark(poolp); if (!mark) goto loser; #if 0 /* sanity check */ keyType = SECKEY_GetPublicKeyType(publickey); PORT_Assert(keyType == rsaKey); if (keyType != rsaKey) { goto loser; } #endif /* allocate memory for the encrypted key */ #if USE_CDSA_CRYPTO rv = SecKeyGetStrengthInBits(publickey, NULL, &data_len); if (rv) goto loser; // Convert length to bytes; data_len >>= 2; #else data_len = SecKeyGetSize(publickey, kSecKeyEncryptedDataSize); #endif encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len); encKey->Length = data_len; if (encKey->Data == NULL) goto loser; /* encrypt the key now */ rv = WRAP_PubWrapSymKey(publickey, bulkkey, encKey); if (rv != SECSuccess) goto loser; PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: if (mark) { PORT_ArenaRelease(poolp, mark); } return SECFailure; }
bool cert_key_is_rsa(CERTCertificate *cert) { bool ret = FALSE; SECKEYPublicKey *pk = SECKEY_ExtractPublicKey( &cert->subjectPublicKeyInfo); if (pk != NULL) { ret = SECKEY_GetPublicKeyType(pk) == rsaKey; SECKEY_DestroyPublicKey(pk); } return ret; }
SECStatus NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp, SECKEYPublicKey *publickey, PK11SymKey *bulkkey, SECItem *encKey) { SECStatus rv; int data_len; KeyType keyType; void *mark = NULL; mark = PORT_ArenaMark(poolp); if (!mark) goto loser; /* sanity check */ keyType = SECKEY_GetPublicKeyType(publickey); PORT_Assert(keyType == rsaKey); if (keyType != rsaKey) { goto loser; } /* allocate memory for the encrypted key */ data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */ encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len); encKey->len = data_len; if (encKey->data == NULL) goto loser; /* encrypt the key now */ rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION), publickey, bulkkey, encKey); if (rv != SECSuccess) goto loser; PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: if (mark) { PORT_ArenaRelease(poolp, mark); } return SECFailure; }
/* * smime_choose_cipher - choose a cipher that works for all the recipients * * "scert" - sender's certificate * "rcerts" - recipient's certificates */ static long smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts) { PLArenaPool *poolp; long cipher; long chosen_cipher; int *cipher_abilities; int *cipher_votes; int weak_mapi; int strong_mapi; int aes128_mapi; int aes256_mapi; int rcount, mapi, max, i; chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */ weak_mapi = smime_mapi_by_cipher(chosen_cipher); aes128_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_128); aes256_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_256); 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; /* Make triple-DES the strong cipher. */ strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168); /* 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_QuickDERDecodeItem(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 are RC2-40 (weak crypto) and * 3DES (strong crypto), unless the user has an elliptic curve * key. For elliptic curve keys, RFC 5753 mandates support * for AES 128 CBC. */ SECKEYPublicKey *key; unsigned int pklen_bits; KeyType key_type; /* * 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; key_type = nullKey; if (key != NULL) { pklen_bits = SECKEY_PublicKeyStrengthInBits (key); key_type = SECKEY_GetPublicKeyType(key); SECKEY_DestroyPublicKey (key); key = NULL; } if (key_type == ecKey) { /* While RFC 5753 mandates support for AES-128 CBC, should use * AES 256 if user's key provides more than 128 bits of * security strength so that symmetric key is not weak link. */ /* RC2-40 is not compatible with elliptic curve keys. */ chosen_cipher = SMIME_DES_EDE3_168; if (pklen_bits > 256) { cipher_abilities[aes256_mapi]++; cipher_votes[aes256_mapi] += pref; pref--; } cipher_abilities[aes128_mapi]++; cipher_votes[aes128_mapi] += pref; pref--; cipher_abilities[strong_mapi]++; cipher_votes[strong_mapi] += pref; pref--; } else { 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; /* 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; }