2

I have a .p8 file download from Apple's iOS developer portal for PushNotifications.

I am trying to load the P8 file with the following code in Python:

from ctypes import *
OpenSSL = cdll.LoadLibrary("/opt/local/lib/libssl.1.0.0.dylib")


def loadPrivateKey(path):
    bio = OpenSSL.BIO_new_file(path.encode("utf-8"), "rb".encode("utf-8"))
    #pKey = OpenSSL.PEM_read_bio_PrivateKey(bio, None, None, None)
    OpenSSL.BIO_free(bio)

def main():
    loadPrivateKey("/users/Brandon/Desktop/APNsAuthKey.p8")

main()

However, it seg faults on the line: OpenSSL.BIO_free(bio). I have checked if bio has a value other than 0 (it does).

If I do the same thing in C, it works:

struct EVP_PKEY* loadPrivateKey(const char* path)
{
    struct BIO* bio = BIO_new_file(path, "rb");
    struct EVP_PKEY* pKey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
    BIO_free(bio);
    return pKey;
}

int main()
{
    struct EVP_PKEY* pKey = loadPrivateKey("/users/Brandon/Desktop/APNsAuthKey.p8");
    EVP_PKEY_free(pKey);
}

I have verified in C that the code works and I have used it to sign data. I have been unable to do the same in Python3 because freeing the BIO segfaults with code 11.

I have tried pyOpenssl, and it also segfaults when I try to read the key with loadprivatekey(FILETYPE_PEM, key) where key is the contents of the P8 file.

Any ideas why it would segfault?

2
  • Which version of OpenSSL are you supplying, and which version of OpenSSL does Python expect? OpenSSL 1.0.2 and OpenSSL 1.1.0 are not binary compatible. Commented Feb 13, 2017 at 2:42
  • @jww; I solved it. After trying for hours.. It turns out you need to specify the types and the types must be a perfect match. Otherwise it fails. For some odd reason pyOpenSSL segfaults even though it is using ffi. I ended up using the solution I posted since none of the third party libraries work atm and I can't find any other solution. Commented Feb 13, 2017 at 2:52

1 Answer 1

2

In case anyone else is having the same issues.. You MUST specify the argtypes and restype. To do that, you need to assign the function pointer to a temporary variable, specify the types and then call it using the temporary.

Example:

from ctypes import *
OpenSSL = cdll.LoadLibrary("/opt/local/lib/libssl.1.0.0.dylib")


def BIO_new_file(path):
    BIO_new_file_func = OpenSSL.BIO_new_file
    BIO_new_file_func.argtypes = [c_char_p, c_char_p]
    BIO_new_file_func.restype = c_void_p
    return BIO_new_file_func(path.encode("utf-8"), "rb".encode("utf-8"))

def BIO_free(bio):
    BIO_free_func = OpenSSL.BIO_free
    BIO_free_func.argtypes = [c_void_p]
    BIO_free_func.restype = None
    return BIO_free_func(bio)


def loadPrivateKey(path):
    bio = BIO_new_file(path)
    #pKey = PEM_read_bio_PrivateKey(bio, None, None, None)
    BIO_free(bio)
    #return pKey

def main():
    loadPrivateKey("/users/Brandon/Desktop/APNsAuthKey.p8")

main()

I was under the impression that I just had to call the functions with the right arguments and it'd work but I was wrong. You have to specify the types! Otherwise use FFI and make your life easier.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.