1

I'm having an issue with regards to a combination of Xamarin and X509 certificates.

We have an app that needs to generate a .PFX file using the BouncyCastle library. The file is generated successfully and decoded in a simple .NET console app without error, to communicate with an MQTT message broker on AWS IoT. Then we moved the code over to Xamarin. After some code changes, we managed to get the Android version working and communicating with the MQTT message broker. However, it fails when running on iOS, specifically when a new X509Certificate2 object is created using the path to the .PFX file and the appropriate password, using this code:

X509Certificate2 clientCertPfx = new X509Certificate2(Path.Combine(folderPath, certificatePathPfx), password);

It fails with the error message: CryptographicException: Unable to decode certificate

This error only happens with a Xamarin.iOS build; the Xamarin.Android build runs as expected. We followed the stack trace to the following code sample that match up with the error message. It would seem like the .PFX file is not being parsed / read properly. I'm wondering if there's anyone out there who knows whether or not there's a way around this error. Is this a shortcoming in the Mono implementation of the X509Certificate2 methods? Is there a chance that the BouncyCastle Pcks12Builder exported the .PFX file incorrectly or something?

Any help would be appreciated, and I'm happy to offer up any additional code as needed.

EDIT 1 Here's the code I use to generate the .PFX file.

var pfxStore = new Pkcs12StoreBuilder()
                    .SetUseDerEncoding(true)
                    .Build();

StreamReader reader = File.OpenText(cert_file_pem); // signed PEM certificate file
var pemReader = new PemReader(reader);

List<X509CertificateEntry> chain = new List<X509CertificateEntry>();

AsymmetricCipherKeyPair privKey = null;

object o;
while ((o = pemReader.ReadObject()) != null)
{
    if (o is Org.BouncyCastle.X509.X509Certificate)
    {
        chain.Add(new X509CertificateEntry((Org.BouncyCastle.X509.X509Certificate)o));
    }
    else if (o is AsymmetricCipherKeyPair)
    {
        privKey = (AsymmetricCipherKeyPair)o;
    }
}

if (privKey == null) 
{
    using (StreamReader privKeyReader = File.OpenText(key_file)) // private key file
    {
        privKey = (AsymmetricCipherKeyPair)new PemReader(privKeyReader).ReadObject();
    }
}

pfxStore.SetKeyEntry("convert", new AsymmetricKeyEntry(privKey.Private), chain.ToArray());
MemoryStream pfxMemoryStream = new MemoryStream();
pfxStore.Save(pfxMemoryStream, thingName.ToCharArray(), new SecureRandom());

var result = Pkcs12Utilities.ConvertToDefiniteLength(pfxMemoryStream.ToArray(), password.ToCharArray());

FileStream pfxFileStream = File.Create(certificatePathPfx);
pfxFileStream.Write(result);

pfxFileStream.Close();
pfxMemoryStream.Close();

EDIT 2 I've also noticed some strange behaviour with regards to the following code:

X509Certificate2 clientCertPfx = new X509Certificate2(certificatePathPfx, password);
Console.WriteLine("Cert type for PFX doc (data): " + X509Certificate2.GetCertContentType(clientCertPfx.RawData));
Console.WriteLine("Cert type for PFX doc (path): " + X509Certificate2.GetCertContentType(certificatePathPfx));

When running this piece of code in a .NET console app, I get the following output:

Cert type for PFX doc (data): Cert
Cert type for PFX doc (path): Pfx

It seems like the X509Certificate2.GetCertContentType() function picks up different content types based on whether the function is called with the path of the file / file name, or the raw file data.

TFSM
  • 53
  • 1
  • 9
  • If you've built the PFX manually, you probably built it in a way that iOS doesn't recognize. IIRC it needs to be `PFX { Non-Encrypted Contents { shrouded private keys }, Encrypted Contents { certificates }, MAC present }`. Deviating from that (unencrypted certificates, unshrouded keys, shrouded keys in the encrypted contents bucket, no MAC) usually results in macOS (and probably iOS) failing to read it properly. – bartonjs Dec 09 '19 at 16:29
  • Hi @bartonjs, thanks for your answer. Do you perhaps have a code snippet or something that we can use set up the file in such a format? Currently we're just using the standard BouncyCastle Pkcs12Store to create the file. Is there a way to modify the store builder to allow for such a format? – TFSM Dec 10 '19 at 06:22
  • Have you tried to take a look at this? https://stackoverflow.com/questions/34503063/unable-to-decode-certificate-at-client-new-x509certificate2 – Saamer Dec 10 '19 at 09:32
  • 1
    any update on this? I'm having the same error – Tim Feb 25 '21 at 20:37

0 Answers0