Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift (Linux): Extract CMS/PKCS#7 Certs and Validate Container Signature?

I am writing a set of services in Swift 4 that will run on Linux. One of the things I need to do is receive a payload that is digitally signed using the Cryptographic Message Syntax (CMS) format, extract the certificate used to sign it, and then validate the signature. I know that Swift on Linux doesn't contain a Security or CommonCrypto framework for this sort of thing, so I've linked in OpenSSL to try and help with this. I'm about 2 years removed from my C/C++ programming days, so I readily admit I'm in over my head on this portion of the code.

I have 2 simple classes to act as wrappers for OpenSSL BIO and PKCS7 data structures. They look like this:

import Foundation
import OpenSSL

public final class BIOWrapper {

    public var bio = BIO_new(BIO_s_mem())

    public init(data: Data) {
        data.withUnsafeBytes { pointer -> Void in
            BIO_write(self.bio, pointer, Int32(data.count))
        }
    }

    public init() {}

    deinit {
        BIO_free(self.bio)
    }
}

public final class PKCS7Wrapper {

    public var pkcs7: UnsafeMutablePointer<PKCS7>

    public init(pkcs7: UnsafeMutablePointer<PKCS7>) {
        self.pkcs7 = pkcs7
    }

    deinit {
        PKCS7_free(self.pkcs7)
    }
}

I am able to successfully extract the PKCS#7 container data and validate that the data type code value is NID_pkcs7_signed using this code:

let reqData = Data(bytes: reqBytes)
        guard reqData.count > 0 else {
            print("Empty request body")
            return nil
        }

        let bioWrapper = BIOWrapper(data: reqData)
        guard let container = d2i_PKCS7_bio(bioWrapper.bio, nil) else {
            print("No container")
            return nil
        }

        let pkcs7Wrapper = PKCS7Wrapper(pkcs7: container)
        let dataTypeCode = OBJ_obj2nid((pkcs7Wrapper.pkcs7.pointee.d.sign).pointee.contents.pointee.type)
        print("dataTypeCode : \(dataTypeCode)")

        if dataTypeCode == NID_pkcs7_data {
            print("GOT DATA!")
        } else {
            print("Didn't get data")
            return nil
        }

       let pkcs7SignedTypeCode = OBJ_obj2nid(pkcs7Wrapper.pkcs7.pointee.type)
        if let signed = pkcs7SignedTypeCode == NID_pkcs7_signed {
            print("Signed : \(signed)")
        }

However, I've now reached a point where I'm stuck. How can I obtain the X.509 certificate data from the PKCS#7 payload? I can see that the pkcs7Wrapper.pkcs7.pointee.d.sign.pointee.cert data structure should contain the certificate chain data. Its data type is UnsafeMutablePointer<stack_st_x509> and I think I can figure out the code to use OpenSSL's PKCS7_verify method once I get the X.509 certificate data in memory. I just don't know how to do THAT part.

I found this resource that talks about validating receipts on OSX/iOS that touches on a lot of the same issues. They obtain the X.509 certificate from the file system and pass the data into the PKCS7_verify method. I just need to know how to get the certificate data from the PKCS#7 container to pass in.

Can anyone help me with this? I recognize that calling C from Swift is not ideal, but in the absence of a good security/cryptography framework for Swift I'm not aware of any other options.

like image 294
Shadowman Avatar asked Mar 23 '18 14:03

Shadowman


1 Answers

The core part of the answer is in the code you linked:

let store = X509_STORE_new()
X509_STORE_add_cert(store, appleRootX509)
OpenSSL_add_all_digests()
let result = PKCS7_verify(receiptPKCS7, nil, store, nil, nil, 0)
if result != 1 {
    log.atLevelDebug(id: 0, source: "Main", message: "Receipt signature verification failed")
    exit(errorCode)
}

What you seem to be missing is the fact that you don't have to extract the X509 certificate from the PKCS7 data yourself. The PKCS7_verify function will do it as part of verification:

An attempt is made to locate all the signer's certificates, first looking in the certs parameter (if it is not NULL) and then looking in any certificates contained in the p7 structure itself. If any signer's certificates cannot be located the operation fails.

Therefore the only certificate you need to load yourself is the root certificate which you have observed they load from the file system in the linked code.

If you still really need a Swift solution to extract the certificate out of the PKCS7 data for some reason, you will have to build an ASN.1 parser for PKCS7. Not sure if this is readily available for Swift, this simple code is what a quick search yielded, and this is a nice description of the PKCS7 data.

like image 80
mnistic Avatar answered Oct 23 '22 21:10

mnistic