QT 5.15 QSSLCertificate::importpkcs12 returns false, how do I debug the reason?
-
Hello, I'm trying to create an oauth2 authentication with certification flow.
I have the following code:
// Load client certificate and key from PFX file QFile pfxFile(certPath); bool is_Open = pfxFile.open(QFile::ReadOnly); Q_ASSERT(is_Open); QSslKey key; QSslCertificate certificate; QList<QSslCertificate> caCertificates; QString version = QSslSocket::sslLibraryBuildVersionString(); qInfo() << version; // Prints "OpenSSL 1.1.1g 21 Apr 2020" if (QSslCertificate::importPkcs12(&pfxFile, &key, &certificate, &caCertificates, certPassword)) { qDebug() << "Loaded PFX file"; } else { qDebug() << "Failed to load PFX file"; return; }
certPath is just my file name, "mycert.pfx". In debug I check the content of pfxFile once opened and it seems correct to me, so the proper file is opened.
certPassword seems correct as well.
Yet the function returns false and I have no idea what I can do to know what exactly went wrong? I have a python program that runs the same process which works with these inputs, so something is somehow different...
Is there a way to know what exactly went wrong in this process when running the importpkcs12 function?
Help is appreciated!
-
@Dadde
Well the first thing to verify isis_Open
, we do not know whether your path is correct. We don't know where relative path"mycert.pfx"
resolves to in your application. At least put in, say, aQ_ASSERT(is_Open)
so people know for sure. -
@Dadde I still have the issue though that importPkcs12 function fails. Is there any way to know exactly what part of the function is failing? Is it because I pass pfxFile as a QFile when the input parameter is actually QIODevice, even though QFile is inheriting QIODevice and therefore seems to compile?
Or is the password of the wrong type? Here's how I declare it in the related header:
QByteArray certPassword = "mypassword";
-
@Dadde said in QT 5.15 QSSLCertificate::importpkcs12 returns false, how do I debug the reason?:
Is it because I pass pfxFile as a QFile when the input parameter is actually QIODevice, even though QFile is inheriting QIODevice and therefore seems to compile?
No, that is fine.
I do not have an answer to how you discover exactly what is wrong. -
Hi,
Which version of Qt are you using ?
On which platform ?
Would it be possible for you to share how one can generate such a certificate to test ?
The Python code you are using as well so people can double check in conditions similar as yours. -
Have you installed the relevant OpenSSL libraries into the runtime path of your executable?
Have you provided the correct password for the input file? -
@SGaist I'm using QT version 5.15.2, visual studio
I don't know myself how to create the certificate since I'm new to these topics but I will see what I can accomplish after the weekend.
I think I can provide the python code though:
import base64 import requests import jwt import hashlib import uuid from datetime import datetime, timedelta from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.serialization import pkcs12 token_endpoint = 'https://xxx.yyy/zzz/oauth2/token' # ADFS endpoint URL authority = 'https://xxx.yyy/zzz' # PFX certificate file path and password cert_file = 'C:/xxx/yyy/zzz.pfx' cert_password = 'password' # Client ID and Secret for your registered application on ADFS client_id = 'xxx-yyy-zzz-vvv-www' # Define the scope of the access token scope = ['https://xxx.yyy.zzz.vvv'] # Load the PFX certificate with open(cert_file, 'rb') as f: pfx_data = f.read() # Load the PFX data and extract the private key private_key, cert, ca_certs = pkcs12.load_key_and_certificates(pfx_data, cert_password.encode(), default_backend()) def hex_string_readable(bytes): return ["{:02X}".format(x) for x in bytes] fingerprint = hex_string_readable(cert.fingerprint(hashes.SHA1())) print(",".join(str(element) for element in fingerprint)) # Serialize the private key to PEM format private_key_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption() ) # Convert bytes to string private_key_pem_str = private_key_pem.decode() print(private_key_pem_str) # Create a client assertion now = datetime.utcnow() expiry = now + timedelta(minutes=10) # Set expiry time for 10 minutes from now kid = "".join(str(element) for element in fingerprint) cert_sha1 = hashlib.sha1() cert_sha1.update(cert.public_bytes(serialization.Encoding.DER)) # Convert the SHA-1 hash to base64 x5t = base64.urlsafe_b64encode(cert_sha1.digest()).rstrip(b'=').decode() claims = { "aud": token_endpoint, "iss": client_id, "sub": client_id, "jti": str(uuid.uuid4()), "exp": expiry, } header = { "kid": kid, "x5t": x5t } # Encode and sign the JWT using the private key client_assertion_bytes = jwt.encode(claims, private_key_pem_str, algorithm='RS256', headers=header) # Prepare data for token request token_request_data = { 'grant_type': 'client_credentials', 'client_id': client_id, 'scope': scope, 'client_assertion_type': 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', 'client_assertion': client_assertion_bytes, } # Send token request to ADFS token endpoint response = requests.post(token_endpoint, data=token_request_data) # Check if the token request was successful if response.status_code == 200: # Print the access token access_token = response.json()['access_token'] print("Access token:", access_token) else: print("Failed to acquire token:", response.text) url = "https://xxx.yyy.zzz.vvv/v2/data.xml" payload = "Change to your request" headers = { 'Content-Type': 'application/xml', 'Authorization': 'Bearer '+access_token } response = requests.request("POST", url, headers=headers, data=payload) print(response.text)
-
@ChrisW67 My hope was that the QT framework has the tools I need to accomplish oauth2 communication(with certification) and it seems that it does, at least for this step with creating the pkcs12 file? Will I still need OpenSsl?
-
@Dadde I did some reading and I think I already have openssl configured since I previously included QtNetwork which should contain these tools: https://doc.qt.io/qt-5/ssl.html
-
@Dadde
I do not use SSL nor build Qt from source, so I could easily be incorrect here. But I think you have to do something/check that you do have OpenSSL installed and found from Qt app.I think they come with OS or Qt binary installer or you download or build them yourself. @ChrisW67 asked you above
Have you installed the relevant OpenSSL libraries into the runtime path of your executable?
What OpenSSL libraries do you have where on what platform, and how is your program run such that it will locate and load them?
-
@JonB @ChrisW67 I haven't "explicitly" installed openssl, so I'm not 100% sure myself, but there is a QSslSocket::sllLibraryBuildVersionString() function that tells the openssl version found and used. I think it is fairly safe to say it is what is used for the QSslCertificate() function as well since it is part of the QTNetwork module which I have installed. I think it is the closest I can get to know if openssl is used?
Here's what the function says when I run it in my program: "OpenSSL 1.1.1g 21 Apr 2020".
I will update the code in the main post.
-
@Dadde Update: I added "Q_ASSERT(QSslSocket::supportsSsl());" and it returns false, so I guess I haven't actually properly implemented openssl then, also previous function "QSslSocket::sllLibraryBuildVersionString()" only checks what version it finds during compile time, "QSslSocket::sslLibraryVersionNumber();" is for runtime and it returns "0". I will look into implementing openssl properly and update if it solved my issue.
-
QSslSocket::sllLibraryBuildVersionString() "Returns the version string of the SSL library in use at compile time. " That is, the version of the OpenSSL libraries that the Qt binaries you are using were built to match. This does not mean those libraries are present in your runtime environment: they are detected and used if present.
So, your runtime environment needs to have OpenSSL 1.1.1 libraries. You may be able to access a compatible version through the Qt Online Installer. Ensuring it is in the run time environment (application binary directory or PATH on Windows, LD_LIBRARY_PATH on Linux etc.) is largely up to you.
7/14