Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumblekit.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikkel Krautz <mikkel@krautz.dk>2013-09-17 05:25:57 +0400
committerMikkel Krautz <mikkel@krautz.dk>2013-09-17 05:25:57 +0400
commit1b4e604f27ff64f4ade69f6ab9855dec21b69f9c (patch)
treed68d9d7ef4c821b0a53b19995d0ac6645e1bd15a
parenta068929ec27d8da99edbe29e6239d25f81721a72 (diff)
MKCertificate: add +certificatesWithPKCS12:password: method; export raw data getters for certificate and private key.
-rw-r--r--src/MKCertificate.m113
-rw-r--r--src/MumbleKit/MKCertificate.h30
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
///--------------------------------