Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect to MQTT with .pem certificate #546

Open
PrLion opened this issue Aug 17, 2023 · 34 comments
Open

Connect to MQTT with .pem certificate #546

PrLion opened this issue Aug 17, 2023 · 34 comments

Comments

@PrLion
Copy link

PrLion commented Aug 17, 2023

Do we have any options for connect to MQTT with cert.pem, privateKey.pem and ca.pem ?
Like I see In documentation we should use only .p12.

In the documentation you has method getClientCertFromP12File()
That method receive .p12 and extract kSecImportItemIdentity (certificate)

But what we should do if we have private key and ca inside for authentication?

@matthew-ely
Copy link

Also interested in a solution.

@MelnykovDenys
Copy link

also faced this problem

@MelnykovDenys
Copy link

@PrLion @matthew-ely
Guys, do you have any solutions?

@PrLion
Copy link
Author

PrLion commented Sep 19, 2023

@MelnykovDenys
No solutions with CocoaMQTT at this moment ;(

@wtdu
Copy link

wtdu commented Sep 22, 2023

@MelnykovDenys @PrLion
Firstly, you need to use openssl command ,in order to convert pem to der. 👇🏻

openssl x509 -outform der -in certificate.pem -out certificate.der

And There is my sample code . Now I can connect mqtt broker And send message to MQTTX Client , but I can't received any message, cocoMQTT api not any response . you can try to debug with my demo code.

MQTT_Test 2.zip

@matthew-ely
Copy link

I still have not found a solution for authentication with certificate, private key and ca files, SO instead we decided to just use openssl to generate a p12 file serverside and request the p12 file bytes and password. I wrote a function mimicking the sample code p12 decryption but takes a byte array and password as arguments:

'''
private func getClientCertFromByteArray(bytes: [UInt8], certPassword: String) -> CFArray? {

    // create key dictionary for reading p12 file
    let key = kSecImportExportPassphrase as String
    let options : NSDictionary = [key: certPassword]

    var items : CFArray?
    let securityError = SecPKCS12Import(NSData(bytes: bytes, length: bytes.count), options, &items)

    guard securityError == errSecSuccess else {
        if securityError == errSecAuthFailed {
            print("ERROR: SecPKCS12Import returned errSecAuthFailed. Incorrect password?")
        } else {
            print("Failed to decode bytes")
        }
        return nil
    }

    guard let theArray = items, CFArrayGetCount(theArray) > 0 else {
        return nil
    }

    let dictionary = (theArray as NSArray).object(at: 0)
    guard let identity = (dictionary as AnyObject).value(forKey: kSecImportItemIdentity as String) else {
        return nil
    }
    let certArray = [identity] as CFArray

    return certArray
}

'''

@matthew-ely
Copy link

@MelnykovDenys @PrLion Firstly, you need to use openssl command ,in order to convert pem to der. 👇🏻

openssl x509 -outform der -in certificate.pem -out certificate.der

And There is my sample code . Now I can connect mqtt broker And send message to MQTTX Client , but I can't received any message, cocoMQTT api not any response . you can try to debug with my demo code.

MQTT_Test 2.zip

I'm afraid I am not permitted to unzip any files so I cannot look it over, but my MQTT broker with AWS works properly now so I can share mindnumbing issues I ran into while implementing the socket.

  • Make sure these properties are enabled prior to connecting: 'mqttClient.enableSSL = true' and 'mqttClient.allowUntrustCACertificates = true' (if your certificates are self signed)
  • If your MQTT connection connects successfully and immediately disconnects, it is likely you are not using a unique Client ID, so use some format like 'let CLIENT_ID = "your_company" + UUID().uuidstring' to ensure a unique ID is always being used.
  • Ensure you are subscribing to all relevant topics upon successful connect and enable 'mqttClient.autoReconnect = true' in case your connection is prone to dropping.
  • Lastly, if you connect and do not receive any messages, ensure another connection is publishing messages to the same topic your device is subscribed to.

@PrLion
Copy link
Author

PrLion commented Sep 22, 2023

@wtdu
Your code isn't work. You are publishing it in the space.
That is a reason why you don't receive any message.

@matthew-ely
If you are using just 1 certificate for connection your mrthod from documentation should work, but if you are use Key, Certificate and CA for connection it won't.

For example: You can create certificate self.

let pem = """
-----BEGIN CERTIFICATE-----
MIIC2jCCAkMCAg38MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG
(...)
+tZ9KynmrbJpTSi0+BM=
-----END CERTIFICATE-----
"""
`// remove header, footer and newlines from pem string`
let certData = Data(base64Encoded: pemWithoutHeaderFooterNewlines)!
``
guard let certificate = SecCertificateCreateWithData(nil, data as CFData) else {
` // handle error`
`}`

but in method private func getClientCertFromByteArray(bytes: [UInt8], certPassword: String) -> CFArray?
you are trying extract SecCertificate

it will not work.

@wtdu
Copy link

wtdu commented Sep 23, 2023

@PrLion @matthew-ely
I guessed the ‘ getClientCertFromByteArray ’ method only support ' two way certificate ' with P12 file ,so I ignored it.

Otherwise,I want to connect mqtt broker by One way cartificate , I needn't to use P12 file.

as your question ‘That is a reason why you don't receive any message.’ which I didn't find reason.

@PrLion
Copy link
Author

PrLion commented Sep 23, 2023

Try to provide your certificate like CFArray

var sslSettings: [String: NSObject] = [:]
sslSettings[kCFStreamSSLCertificates as String] = clientCertArray

    mqtt.sslSettings = sslSettings

@JaylinYu
Copy link
Member

@leeway1208 we should provide an example

@PrLion
Copy link
Author

PrLion commented Sep 28, 2023

With 3 certificates please.
Cert, key and ca.

@PrLion
Copy link
Author

PrLion commented Oct 6, 2023

@leeway1208 @JaylinYu
Hello,
What about example? How could we wrap all three or two certificates for connection?

@leeway1208
Copy link
Collaborator

@leeway1208 @JaylinYu Hello, What about example? How could we wrap all three or two certificates for connection?

I will do some example when I'm not busy these days 😭😭

@leeway1208
Copy link
Collaborator

How about converting *.pem files to *.p12 file?

@MelnykovDenys
Copy link

@leeway1208
I didn't find a way how to do it in code. If you would show us an example we would be very grateful

@PrLion
Copy link
Author

PrLion commented Oct 9, 2023

What do you mean "*.pem files to *.p12 file"
In your code you are extract .pem from .p12

// create key dictionary for reading p12 file
let key = kSecImportExportPassphrase as String
let options : NSDictionary = [key: certPassword]

var items : CFArray?
let securityError = SecPKCS12Import(NSData(bytes: bytes, length: bytes.count), options, &items)

guard securityError == errSecSuccess else {
    if securityError == errSecAuthFailed {
        print("ERROR: SecPKCS12Import returned errSecAuthFailed. Incorrect password?")
    } else {
        print("Failed to decode bytes")
    }
    return nil
}

guard let theArray = items, CFArrayGetCount(theArray) > 0 else {
    return nil
}

let dictionary = (theArray as NSArray).object(at: 0)
guard let identity = (dictionary as AnyObject).value(forKey: kSecImportItemIdentity as String) else {
    return nil
}
let certArray = [identity] as CFArray

return certArray

}

@MelnykovDenys
Copy link

@PrLion
We have 3 files: cert.pem, privateKey.pem (I have RSA PRIVATE KEY) and ca.pem. We don't understand how we can bind these files (maybe to array as CFArray) and set to sslSettings

@PrLion
Copy link
Author

PrLion commented Oct 9, 2023

@leeway1208 @MelnykovDenys
I'm in the same situation.

@leeway1208
Copy link
Collaborator

leeway1208 commented Oct 11, 2023

Using OpenSSL, which you can download at www.openssl.org. The following instructions assume that you retain the default certificate filename of "cert_key_pem.txt."

  1. Open a command prompt and navigate to the directory that contains the cert_key_pem.txt file.

  2. Execute the following OpenSSL command to create a PKCS12 (.p12) file:

openssl pkcs12 -export -inkey cert_key_pem.txt -in cert_key_pem.txt -out cert_key.p12

Note: To convert a PKCS12 certificate to PEM, use the following command:

openssl pkcs12 -in cert_key.p12 -out cert_key.pem -nodes

  1. After you enter the command, you'll be prompted to enter an Export Password. Choose a password or phrase and note the value you enter

  2. A file called cert_key.p12 is created in this directory. This is your .p12 file.

@PrLion
Copy link
Author

PrLion commented Oct 13, 2023

Okay and then we should insert it in code, right?
Then you method will extract cert.pem from .p12 and use it for connection
But we need 3 certificates cert, key and ca.

Thanks and Regards

@PrLion
Copy link
Author

PrLion commented Oct 19, 2023

@leeway1208

@JaylinYu
Copy link
Member

JaylinYu commented Oct 19, 2023

he has already told you the way out. Convert your files into p12, and put that file into sslsettings. Then connect and see how does it work.while you can learn more knowledge about TLS from wiki.

he Will provide a general tls example base on x509 certificate.

@PrLion
Copy link
Author

PrLion commented Oct 19, 2023

@JaylinYu we tested it.

@wtdu wrote it above:

Firstly, you need to use openssl command ,in order to convert pem to der. 👇🏻

openssl x509 -outform der -in certificate.pem -out certificate.der

And There is my sample code . Now I can connect mqtt broker And send message to MQTTX Client , but I can't received any message, cocoMQTT api not any response . you can try to debug with my demo code.

@JaylinYu
Copy link
Member

As long as your client gets CONNACK and trigger the connect_cb, it proves MQTT connection is already working. Plz check Broker side on receiving issue, verify there is a Publish msg to client first.

@PrLion
Copy link
Author

PrLion commented Oct 19, 2023

No MQTT connection doesn't work. Because I tested it on Android and it works.
I asked you to help us, because we can't to figure out how to connect with three certificates.

@leeway1208
Copy link
Collaborator

Hello. you can set the x509 certificate like this.

      func readX509Certificates() -> CFArray? {
        guard let certURL = Bundle.main.url(forResource: "certificate", withExtension: "cer") else {
            print("Certificate file not found")
            return nil
        }

        do {
            let certData = try Data(contentsOf: certURL)
            let certOptions: NSDictionary = [kSecImportExportPassphrase as NSString: ""]

            var rawItems: CFArray?
            let status = SecPKCS12Import(certData as CFData, certOptions, &rawItems)

            if status == errSecSuccess, let items = rawItems {
                return items
            } else {
                print("Failed to import certificate. Status code: \\(status)")
                return nil
            }
        } catch {
            print("Failed to read certificate data: \\(error)")
            return nil
        }
    }

/// this shows how to use function
   if let certificates = readX509Certificates() {
        for index in 0..<CFArrayGetCount(certificates) {
            let certificate = unsafeBitCast(CFArrayGetValueAtIndex(certificates, index), to: SecCertificate.self)
            var sslSettings: [String: NSObject] = [:]
            sslSettings[kCFStreamSSLCertificates as String] = clientCertArray

        }
    }

@PrLion
Copy link
Author

PrLion commented Oct 27, 2023

On my side we are receiving .pem certificates by API, and we can't convert it to .p12.
How could I use cert.pem and key for connection.

Could you provide some example?

CC: @leeway1208 @JaylinYu

@PrLion
Copy link
Author

PrLion commented May 21, 2024

@leeway1208 Any updates

@matthew-ely
Copy link

@PrLion Converting the .pem file from the API to .p12 requires OpenSSL Im fairly certain. A quick google search yields Swift packages that contain OpenSSL C functions for iOS that could solve the problem but I have not tested the functionality or security of these techniques.

@PrLion
Copy link
Author

PrLion commented May 21, 2024

@matthew-ely could you share that library please ?

@matthew-ely
Copy link

@PrLion https://github.com/krzyzanowskim/OpenSSL <-- package. Again I have not tested this package's functionality or security.

@PrLion
Copy link
Author

PrLion commented May 21, 2024

Got it. Really, I don't understand why such a powerful team can't implement the connection in different ways?
@matthew-ely @leeway1208

@PrLion
Copy link
Author

PrLion commented Oct 3, 2024

Hello @matthew-ely @leeway1208
Any solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants