Hi, > CMS_verify() works fine if you have the signer cert, but now we have > a CMS file for which only the (trusted) signer public key is available. > > Q: is there a high level function like CMS_verify() that works with a > public key? > > If not: what would be the best alternative for us? > - Rewrite the CMS_verify() function to use public keys? > - Create a cert (with fake signature) with the public key?
In case someone should have to do the same: below is the code to make a fake cert (if only 1 signerInfo is present). After that you can call CMS_Verify() with flags = CMS_NO_SIGNER_CERT_VERIFY. There's another catch: CMS_verify() changes the CMS_ContentInfo struct in the sense the signer cert is added if it's not already present, so subsequence CMS_verify() calls will keep using the added cert. Don't know if that's intentional, but in our case, where we read candidate certs/pubkeys from disk and try them one by one, we had to work around this. Cheers, Stef // E.g. of pubKey: // 30 81 9f // 30 0d // 06 09 2a 86 48 86 f7 0d 01 01 01 // 05 00 // 03 81 8d // 00 // 30 81 89 // 02 81 81 00 83 b3 ba ... 16 41 // 02 03 01 00 01 void *CSOD::pubKey2DummyCert(CMS_ContentInfo *cms, unsigned char *pucPubKey, unsigned long iPubKeyLen) { // Check that we have exactly 1 signerInfo field CMS_SignerInfo *si = GetSignerInfo(cms); if (si == NULL || si->sid == NULL) return NULL; CMS_SignerIdentifier *sid = si->sid; // Convert pucPubKey -> EVP_PKEY BIO *bio = BIO_new_mem_buf(pucPubKey, iPubKeyLen); if (bio == NULL) return NULL; EVP_PKEY *pubkey = d2i_PUBKEY_bio(bio, NULL); BIO_free(bio); if (pubkey == NULL) return NULL; // Create the unsigned cert X509 *x = X509_new(); if (x == NULL) { EVP_PKEY_free(pubkey); return NULL; } // Add dummy data X509_set_version(x,2); X509_gmtime_adj(X509_get_notBefore(x), 0); X509_gmtime_adj(X509_get_notAfter(x), (long) 60*60*24*10); X509_set_pubkey(x,pubkey); struct X509_name_st *subj = X509_get_subject_name(x); X509_NAME_add_entry_by_txt(subj, "C", MBSTRING_ASC, (const unsigned char *) "BE", -1, -1, 0); X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC, (const unsigned char *) "Dummy", -1, -1, 0); // The signer info (issuer+serialnr or keyid) in the SOD file must be present // in the dummy cert we're making, because the CMS_verify() function needs this if (sid->type == CMS_SIGNERINFO_ISSUER_SERIAL) { X509_set_issuer_name(x, sid->d.issuerAndSerialNumber->issuer); X509_set_serialNumber(x, sid->d.issuerAndSerialNumber->serialNumber); } else if (sid->type == CMS_SIGNERINFO_KEYIDENTIFIER) { if (x->skid != NULL) M_ASN1_OCTET_STRING_free(x->skid); x->skid = M_ASN1_OCTET_STRING_dup(sid->d.subjectKeyIdentifier); // Set a dummy issuer DN and serial nr ASN1_INTEGER_set(X509_get_serialNumber(x), 13); struct X509_name_st *issuer = X509_get_subject_name(x); X509_NAME_add_entry_by_txt(issuer, "C", MBSTRING_ASC, (const unsigned char *) "BE", -1, -1, 0); X509_NAME_add_entry_by_txt(issuer, "CN", MBSTRING_ASC, (const unsigned char *) "Dummy CA", -1, -1, 0); } // Sign the cert with just any private key, // CMS_verify() doesn't check this if flags = CMS_NO_SIGNER_CERT_VERIFY RSA *rsa = RSA_generate_key(512, RSA_F4, NULL, NULL); EVP_PKEY *privkey = EVP_PKEY_new(); if (EVP_PKEY_assign_RSA(privkey, rsa)) { if (!X509_sign(x, privkey, EVP_sha1())) { X509_free(x); x = NULL; } } if (privkey != NULL) EVP_PKEY_free(privkey); return x; } ______________________________________________________________________ OpenSSL Project http://www.openssl.org User Support Mailing List openssl-users@openssl.org Automated List Manager majord...@openssl.org