diff options
author | Mikkel Krautz <mikkel@krautz.dk> | 2013-09-17 05:25:57 +0400 |
---|---|---|
committer | Mikkel Krautz <mikkel@krautz.dk> | 2013-09-17 05:25:57 +0400 |
commit | 1b4e604f27ff64f4ade69f6ab9855dec21b69f9c (patch) | |
tree | d68d9d7ef4c821b0a53b19995d0ac6645e1bd15a | |
parent | a068929ec27d8da99edbe29e6239d25f81721a72 (diff) |
MKCertificate: add +certificatesWithPKCS12:password: method; export raw data getters for certificate and private key.
-rw-r--r-- | src/MKCertificate.m | 113 | ||||
-rw-r--r-- | src/MumbleKit/MKCertificate.h | 30 |
2 files changed, 140 insertions, 3 deletions
diff --git a/src/MKCertificate.m b/src/MKCertificate.m index 089c2c4..bd91ad2 100644 --- a/src/MKCertificate.m +++ b/src/MKCertificate.m @@ -232,6 +232,119 @@ static int add_ext(X509 * crt, int nid, char *value) { return retcert; } +// Transliterated from C++ to C/Objective-C from libmumble's X509CertificatePrivate::FromPKCS12 +// with a minor fix to avoid duplicating the leaf certificate (sha256 comparison). +// TODO(mkrautz): backport the above fix, or one like it, to libmumble. ++ (NSArray *) certificatesWithPKCS12:(NSData *)pkcs12 password:(NSString *)password { + NSMutableArray *out_certs = [NSMutableArray array]; + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + PKCS12 *pkcs = NULL; + BIO *mem = NULL; + STACK_OF(X509) *certs = NULL; + int ret = 0; + + if (!pkcs12) { + return out_certs; + } + + void *buf = (void *) [pkcs12 bytes]; + int len = [pkcs12 length]; + + mem = BIO_new_mem_buf(buf, len); + (void) BIO_set_close(mem, BIO_NOCLOSE); + pkcs = d2i_PKCS12_bio(mem, NULL); + + if (pkcs) { + ret = PKCS12_parse(pkcs, [password UTF8String], &pkey, &x509, &certs); + // If we're using an empty password, try with nullptr as the + // password argument. Some software might have botched this, + // an we need to stay compatible. + if (ret == 0 && [password length] == 0) { + ret = PKCS12_parse(pkcs, NULL, &pkey, &x509, &certs); + } + // We got a certs stack. This means that we're extracting a + // PKCS12 blob that didn't include a private key. + if (ret == 1 && certs != NULL && x509 == NULL && pkey == NULL) { + // Fallthrough to extraction of the chain stack. + + // PKCS12_parse filled out both our x509 and or pkey. This means + // we're extracting a PKCS12 with a leaf certificate, a private key, + // and possibly a certificate chain following the leaf. + } else if (ret == 1 && x509 != NULL && pkey != NULL) { + // Ensure that the leaf and private key match. + if (X509_check_private_key(x509, pkey) > 0) { + int len = i2d_PrivateKey(pkey, NULL); + + NSMutableData *pkey_der = [[NSMutableData alloc] initWithLength:len]; + char *buf = (char *) [pkey_der bytes]; + i2d_PrivateKey(pkey, (unsigned char **)(&buf)); + EVP_PKEY_free(pkey); + + len = i2d_X509(x509, NULL); + NSMutableData *leaf_der = [[NSMutableData alloc] initWithLength:len]; + buf = (char *) [leaf_der bytes]; + i2d_X509(x509, (unsigned char **)(&buf)); + X509_free(x509); + + MKCertificate *cert = [MKCertificate certificateWithCertificate:leaf_der privateKey:pkey_der]; + [out_certs addObject:cert]; + } + } else { + assert(ret == 0); + assert(x509 == NULL); + assert(pkey == NULL); + assert(certs == NULL); + } + // If we found a leaf, and there are extra certs, attempt to extract + // those as well. + if (certs != NULL) { + // If we have no leaf certificate, don't expect there to be any certificates in out_certs + // at this point. + // If we do have a leaf certificate, having no certificates in out_certs is an error (there + // should be exactly one in there). + X509 *leaf = x509; + if (([out_certs count] == 0 && leaf == NULL) || ([out_certs count] == 1 && leaf != NULL)) { + int ncerts = sk_X509_num(certs); + for (int i = 0; i < ncerts; i++) { + x509 = sk_X509_pop(certs); + + int len = i2d_X509(x509, NULL); + NSMutableData *leaf_der = [[NSMutableData alloc] initWithLength:len]; + char *buf = (char *) [leaf_der bytes]; + i2d_X509(x509, (unsigned char **)(&buf)); + + // Avoid inserting duplicates of the leaf certiifcate into out_certs. + // We can't be sure of the order, and we can't do pointer comparisons + // either. Settle for SHA256 equality. + BOOL shouldAdd = YES; + MKCertificate *cert = [MKCertificate certificateWithCertificate:leaf_der privateKey:nil]; + if (leaf) { + MKCertificate *leafCert = [out_certs objectAtIndex:0]; + if ([[cert hexDigestOfKind:@"sha256"] isEqualToString:[leafCert hexDigestOfKind:@"sha256"]]) { + shouldAdd = NO; + } + } + if (shouldAdd) { + [out_certs addObject:cert]; + } + + X509_free(x509); + } + } + } + } + + if (certs) + sk_X509_pop_free(certs, X509_free); + if (pkcs) + PKCS12_free(pkcs); + if (mem) + BIO_free(mem); + + return out_certs; +} + // Export a chain of certificates to a PKCS12-encoded data blob. In most cases, the leaf // certificate is expected to contain a private key, but this is not required. + (NSData *) exportCertificateChainAsPKCS12:(NSArray *)chain withPassword:(NSString *)password { diff --git a/src/MumbleKit/MKCertificate.h b/src/MumbleKit/MKCertificate.h index b33f962..310ae90 100644 --- a/src/MumbleKit/MKCertificate.h +++ b/src/MumbleKit/MKCertificate.h @@ -82,9 +82,23 @@ extern NSString *MKCertificateItemSerialNumber; /// from the given PKCS12 data. + (MKCertificate *) certificateWithPKCS12:(NSData *)pkcs12 password:(NSString *)password; -///--------------------------------- -/// @name Certificate content status -///--------------------------------- +/// Import one or more certificates from a PKCS12 file with the given password. +/// +/// @param pkcs12 A PKCS12-encoded bundle of certificates and possibly also a private key for +/// for one of the certificates. +/// @param password The password to decode the given PKCS12-encoded file. +/// May be nil if no password, or a blank password should be used for decoding +/// the given PKCS12 data. +/// +/// @returns An NSArray of MKCertificates corresponding to the content of the pkcs12 blob. +/// If the pkcs12 blob contained a private key, that private key will be paired with +/// the certificate it corresponds to. +/// The leaf certificate is guaranteed to be at index 0 in the returned NSArray. ++ (NSArray *) certificatesWithPKCS12:(NSData *)pkcs12 password:(NSString *)password; + +///--------------------------------------------- +/// @name Certificate content and content status +///--------------------------------------------- /// Determine whether the certificate has a certificate (and public key) /// @@ -92,12 +106,22 @@ extern NSString *MKCertificateItemSerialNumber; /// Otherwise, returns NO. - (BOOL) hasCertificate; +/// Get a pointer to the NSData object holding the certificate in DER format. +/// +/// @return Returns the DER-formatted certificate underlying this MKCertificate object. +- (NSData *) certificate; + /// Determine whether the MKCertficiate object has private key data. /// /// @returns Returns YES if the MKCertificate object has a private key. /// Otherwise, returns NO. - (BOOL) hasPrivateKey; +/// Get a pointer to the NSData object holding the private key in DER format. +/// +/// @return Returns the DER-formatted private key underlying this MKCertificate object. +- (NSData *) privateKey; + ///-------------------------------- /// @name Exporting a MKCertificate ///-------------------------------- |