common_name -> friendly_name

This commit is contained in:
David Rose 2009-09-18 00:29:53 +00:00
parent df998fbf48
commit 2552864754
6 changed files with 252 additions and 86 deletions

View File

@ -165,7 +165,7 @@ AuthDialog(const wxString &cert_filename, const wxString &cert_dir) :
_verify_result = -1;
read_cert_file(cert_filename);
get_common_name();
get_friendly_name();
verify_cert();
layout();
}
@ -295,39 +295,52 @@ read_cert_file(const wxString &cert_filename) {
}
////////////////////////////////////////////////////////////////////
// Function: AuthDialog::get_common_name
// Function: AuthDialog::get_friendly_name
// Access: Private
// Description: Extracts the common_name from the certificate.
// Description: Extracts the "friendly name" from the certificate:
// the common name or email name.
////////////////////////////////////////////////////////////////////
void AuthDialog::
get_common_name() {
get_friendly_name() {
if (_cert == NULL) {
_common_name.clear();
_friendly_name.clear();
return;
}
// A complex OpenSSL interface to extract out the common name in
// utf-8.
X509_NAME *xname = X509_get_subject_name(_cert);
if (xname != NULL) {
int pos = X509_NAME_get_index_by_NID(xname, NID_commonName, -1);
if (pos != -1) {
// We just get the first common name. I guess it's possible to
// have more than one; not sure what that means in this context.
X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
if (xentry != NULL) {
ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
if (data != NULL) {
// We use "print" to dump the output to a memory BIO. Is
// there an easier way to decode the ASN1_STRING? Curse
// these incomplete docs.
BIO *mbio = BIO_new(BIO_s_mem());
ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
char *pp;
long pp_size = BIO_get_mem_data(mbio, &pp);
_common_name = wxString(pp, wxConvUTF8, pp_size);
BIO_free(mbio);
static const int nid_choices[] = {
NID_pkcs9_emailAddress,
NID_commonName,
-1,
};
// Choose the first NID that exists on the cert.
for (int ni = 0; nid_choices[ni] != -1; ++ni) {
int nid = nid_choices[ni];
// A complex OpenSSL interface to extract out the name in utf-8.
X509_NAME *xname = X509_get_subject_name(_cert);
if (xname != NULL) {
int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
if (pos != -1) {
// We just get the first common name. I guess it's possible to
// have more than one; not sure what that means in this context.
X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
if (xentry != NULL) {
ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
if (data != NULL) {
// We use "print" to dump the output to a memory BIO. Is
// there an easier way to decode the ASN1_STRING? Curse
// these incomplete docs.
BIO *mbio = BIO_new(BIO_s_mem());
ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
char *pp;
long pp_size = BIO_get_mem_data(mbio, &pp);
_friendly_name = wxString(pp, wxConvUTF8, pp_size);
BIO_free(mbio);
return;
}
}
}
}
@ -388,7 +401,7 @@ verify_cert() {
X509_STORE_free(store);
cerr << "Got certificate from " << _common_name
cerr << "Got certificate from " << _friendly_name
<< ", verify_result = " << _verify_result << "\n";
}
@ -461,7 +474,7 @@ get_text(wxString &header, wxString &text) {
break;
case 0:
text.Printf(verified_cert_text, _common_name.c_str(), _common_name.c_str(), _common_name.c_str());
text.Printf(verified_cert_text, _friendly_name.c_str(), _friendly_name.c_str(), _friendly_name.c_str());
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
@ -469,12 +482,12 @@ get_text(wxString &header, wxString &text) {
case X509_V_ERR_CRL_NOT_YET_VALID:
case X509_V_ERR_CRL_HAS_EXPIRED:
header = _T("Expired signature!");
text.Printf(expired_cert_text, _common_name.c_str());
text.Printf(expired_cert_text, _friendly_name.c_str());
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
header = _T("Unverified signature!");
text.Printf(unknown_auth_cert_text, _common_name.c_str());
text.Printf(unknown_auth_cert_text, _friendly_name.c_str());
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:

View File

@ -67,7 +67,7 @@ public:
private:
void read_cert_file(const wxString &cert_filename);
void get_common_name();
void get_friendly_name();
void verify_cert();
void layout();
@ -84,7 +84,7 @@ private:
X509 *_cert;
STACK *_stack;
wxString _common_name;
wxString _friendly_name;
int _verify_result;
};

View File

@ -224,17 +224,19 @@ help() {
" generate slightly smaller files, but compression takes longer. The\n"
" default is -" << default_compression_level << ".\n\n"
" -S file.crt,[chain.crt],file.key[,\"password\"]\n"
" -S file.crt[,chain.crt[,file.key[,\"password\"]]]\n"
" Sign the multifile. The signing certificate should be in PEM form in\n"
" file.crt, with its private key in PEM form in file.key. If the key\n"
" is encrypted on-disk, specify the decryption password as the third\n"
" option. If a certificate chain is required, chain.crt should also\n"
" be specified; note that the commas should be supplied even if this\n"
" optional filename is omitted.\n"
" be specified; note that the separating commas should be supplied\n"
" even if this optional filename is omitted.\n"
" You may also provide a single composite file that contains the\n"
" certificate, chain, and key in the same file.\n"
" PEM form is the form accepted by the Apache web server. The\n"
" signature is written to the multifile to prove it is unchanged; any\n"
" subsequent change to the multifile will invalidate the signature.\n"
" This parameter may be repeated to sign the multifile with different\n"
" This parameter may be repeated to sign the multifile with additional\n"
" certificates.\n\n";
}
@ -391,7 +393,13 @@ add_files(const vector_string &params) {
bool okflag = do_add_files(multifile, filenames);
if (multifile->needs_repack()) {
bool needs_repack = multifile->needs_repack();
if (append) {
// If we specified -r mode, we always repack.
needs_repack = true;
}
if (needs_repack) {
if (!multifile->repack()) {
cerr << "Failed to write " << multifile_name << ".\n";
okflag = false;
@ -529,27 +537,27 @@ sign_multifile() {
vector_string::iterator si;
for (si = sign_params.begin(); si != sign_params.end(); ++si) {
const string &param = (*si);
Filename certificate, chain, pkey;
string password;
size_t comma1 = param.find(',');
if (comma1 == string::npos) {
cerr << "Signing parameter requires two commas: " << param << "\n";
return false;
}
size_t comma2 = param.find(',', comma1 + 1);
if (comma2 == string::npos) {
cerr << "Signing parameter requires two commas: " << param << "\n";
return false;
}
size_t comma3 = param.find(',', comma2 + 1);
Filename certificate = Filename::from_os_specific(param.substr(0, comma1));
Filename chain = Filename::from_os_specific(param.substr(comma1 + 1, comma2 - comma1 - 1));
Filename pkey;
string password;
if (comma3 != string::npos) {
pkey = Filename::from_os_specific(param.substr(comma2 + 1, comma3 - comma2 - 1));
password = param.substr(comma3 + 1);
certificate = Filename::from_os_specific(param);
} else {
pkey = Filename::from_os_specific(param.substr(comma2 + 1));
certificate = Filename::from_os_specific(param.substr(0, comma1));
size_t comma2 = param.find(',', comma1 + 1);
if (comma2 == string::npos) {
chain = Filename::from_os_specific(param.substr(comma1 + 1));
} else {
chain = Filename::from_os_specific(param.substr(comma1 + 1, comma2 - comma1 - 1));
size_t comma3 = param.find(',', comma2 + 1);
if (comma3 == string::npos) {
pkey = Filename::from_os_specific(param.substr(comma2 + 1));
} else {
pkey = Filename::from_os_specific(param.substr(comma2 + 1, comma3 - comma2 - 1));
password = param.substr(comma3 + 1);
}
}
}
if (!multifile->add_signature(certificate, chain, pkey, password)) {
@ -675,7 +683,7 @@ list_files(const vector_string &params) {
if (num_signatures != 0) {
cout << "\n";
for (i = 0; i < num_signatures; ++i) {
cout << "Signed by " << multifile->get_signature_common_name(i);
cout << "Signed by " << multifile->get_signature_friendly_name(i);
int verify_result = multifile->validate_signature_certificate(i);
if (verify_result == 0) {
cout << " (certificate validated)\n";

View File

@ -568,3 +568,14 @@ INLINE Multifile::CertRecord::
~CertRecord() {
X509_free(_cert);
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::CertRecord::Copy Assignment
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void Multifile::CertRecord::
operator = (const Multifile::CertRecord &other) {
X509_free(_cert);
_cert = X509_dup(other._cert);
}

View File

@ -598,6 +598,13 @@ add_signature(const Filename &certificate, const Filename &chain,
const Filename &pkey, const string &password) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
if (chain.empty() && pkey.empty()) {
// If the second two filenames are empty, assume we're going for
// the composite mode, where everything's stuffed into the first
// file.
return add_signature(certificate, password);
}
CertChain cert_chain;
// Read the certificate file from VFS. First, read the complete
@ -673,6 +680,98 @@ add_signature(const Filename &certificate, const Filename &chain,
return result;
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::add_signature
// Access: Published
// Description: Adds a new signature to the Multifile. This
// signature associates the indicated certificate with
// the current contents of the Multifile. When the
// Multifile is read later, the signature will still be
// present only if the Multifile is unchanged; any
// subsequent changes to the Multifile will
// automatically invalidate and remove the signature.
//
// This flavor of add_signature() reads the certificate,
// private key, and certificate chain from the same
// PEM-formatted file. It takes the first private key
// found as the intended key, and then uses the first
// certificate found that matches that key as the
// signing certificate. Any other certificates in the
// file are taken to be part of the chain.
////////////////////////////////////////////////////////////////////
bool Multifile::
add_signature(const Filename &composite, const string &password) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
// First, read the complete file into memory.
string composite_data;
if (!vfs->read_file(composite, composite_data, true)) {
express_cat.info()
<< "Could not read " << composite << ".\n";
return false;
}
// Get the private key.
BIO *pkey_mbio = BIO_new_mem_buf((void *)composite_data.data(), composite_data.size());
EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio, NULL, NULL,
(void *)password.c_str());
BIO_free(pkey_mbio);
if (evp_pkey == NULL) {
express_cat.info()
<< "Could not read private key in " << composite << ".\n";
return false;
}
// Now read all of the certificates.
CertChain cert_chain;
BIO *chain_mbio = BIO_new_mem_buf((void *)composite_data.data(), composite_data.size());
X509 *c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
while (c != NULL) {
cert_chain.push_back(c);
c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
}
BIO_free(chain_mbio);
if (cert_chain.empty()) {
express_cat.info()
<< "Could not read certificates in " << composite << ".\n";
return false;
}
// Now find the certificate that matches the signature, and move it
// to the front of the chain.
size_t i;
bool found_match = false;
for (i = 0; i < cert_chain.size(); ++i) {
X509 *c = cert_chain[i]._cert;
if (X509_check_private_key(c, evp_pkey)) {
found_match = true;
if (i != 0) {
// Move this entry to the beginning.
cert_chain.insert(cert_chain.begin(), cert_chain[i]);
cert_chain.erase(cert_chain.begin() + i + 1);
}
break;
}
}
if (!found_match) {
express_cat.info()
<< "No certificates in " << composite << " match key.\n";
return false;
}
bool result = add_signature(cert_chain, evp_pkey);
EVP_PKEY_free(evp_pkey);
return result;
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::add_signature
@ -756,6 +855,24 @@ add_signature(const Multifile::CertChain &cert_chain, EVP_PKEY *pkey) {
return false;
}
}
if (cert_chain.empty()) {
express_cat.info()
<< "No certificate given.\n";
return false;
}
if (pkey == NULL) {
express_cat.info()
<< "No private key given.\n";
return false;
}
if (!X509_check_private_key(cert_chain[0]._cert, pkey)) {
express_cat.info()
<< "Private key does not match certificate.\n";
return false;
}
// Now encode that list of certs to a stream in DER form.
stringstream der_stream;
@ -871,42 +988,54 @@ get_signature_subject_name(int n) const {
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_signature_common_name
// Function: Multifile::get_signature_friendly_name
// Access: Published
// Description: Returns the "common name" for the nth signature found
// on the Multifile, encoded in utf-8. This is a subset
// of the subject_name, but it is probably a friendlier
// string to present to a user. See the comments in
// get_num_signatures().
// Description: Returns a "friendly name" for the nth signature found
// on the Multifile. This attempts to extract out the
// most meaningful part of the subject name. It returns
// the emailAddress, if it is defined; otherwise, it
// returns the commonName.
// See the comments in get_num_signatures().
////////////////////////////////////////////////////////////////////
string Multifile::
get_signature_common_name(int n) const {
get_signature_friendly_name(int n) const {
const CertChain &cert_chain = get_signature(n);
nassertr(!cert_chain.empty(), string());
// A complex OpenSSL interface to extract out the common name in
// utf-8.
X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
if (xname != NULL) {
int pos = X509_NAME_get_index_by_NID(xname, NID_commonName, -1);
if (pos != -1) {
// We just get the first common name. I guess it's possible to
// have more than one; not sure what that means in this context.
X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
if (xentry != NULL) {
ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
if (data != NULL) {
// We use "print" to dump the output to a memory BIO. Is
// there an easier way to decode the ASN1_STRING? Curse
// these incomplete docs.
BIO *mbio = BIO_new(BIO_s_mem());
ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
static const int nid_choices[] = {
NID_pkcs9_emailAddress,
NID_commonName,
-1,
};
char *pp;
long pp_size = BIO_get_mem_data(mbio, &pp);
string name(pp, pp_size);
BIO_free(mbio);
return name;
// Choose the first NID that exists on the cert.
for (int ni = 0; nid_choices[ni] != -1; ++ni) {
int nid = nid_choices[ni];
// A complex OpenSSL interface to extract out the name in utf-8.
X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
if (xname != NULL) {
int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
if (pos != -1) {
// We just get the first common name. I guess it's possible to
// have more than one; not sure what that means in this context.
X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
if (xentry != NULL) {
ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
if (data != NULL) {
// We use "print" to dump the output to a memory BIO. Is
// there an easier way to decode the ASN1_STRING? Curse
// these incomplete docs.
BIO *mbio = BIO_new(BIO_s_mem());
ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
char *pp;
long pp_size = BIO_get_mem_data(mbio, &pp);
string name(pp, pp_size);
BIO_free(mbio);
return name;
}
}
}
}
@ -2081,6 +2210,8 @@ clear_subfiles() {
delete subfile;
}
_cert_special.clear();
_signatures.clear();
#endif // HAVE_OPENSSL
Subfiles::iterator fi;

View File

@ -89,6 +89,7 @@ PUBLISHED:
INLINE CertRecord(X509 *cert);
INLINE CertRecord(const CertRecord &copy);
INLINE ~CertRecord();
INLINE void operator = (const CertRecord &other);
X509 *_cert;
};
typedef pvector<CertRecord> CertChain;
@ -97,13 +98,15 @@ PUBLISHED:
const Filename &chain,
const Filename &pkey,
const string &password = "");
bool add_signature(const Filename &composite,
const string &password = "");
bool add_signature(X509 *certificate, STACK *chain, EVP_PKEY *pkey);
bool add_signature(const CertChain &chain, EVP_PKEY *pkey);
int get_num_signatures() const;
const CertChain &get_signature(int n) const;
string get_signature_subject_name(int n) const;
string get_signature_common_name(int n) const;
string get_signature_friendly_name(int n) const;
string get_signature_public_key(int n) const;
void write_signature_certificate(int n, ostream &out) const;