xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.64k stars 1.88k forks source link

Unable to decode certificat - X509Certificate2 #15760

Closed assoumemba closed 1 year ago

assoumemba commented 1 year ago

Description

When generating ECDSA certificate got persistent error with Xamarin iOS, but same code works perfectly on Windows 10. Exception message: "Unable to decode certificate." {System.Security.Cryptography.CryptographicException: Unable to decode certificate. at System.Security.Cryptography.X509Certificates.X509Certificate2ImplMono..ctor (System.Byte[] rawData, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Securit…}

Found similar issue on stackoverflow. Not sure if it's related since it looks internal to Mono. https://github.com/dotnet/runtime/issues/23763 https://github.com/xamarin/xamarin-android/issues/5440

Steps to Reproduce

  1. Xamarin.iOS
  2. Sample code:

    var convertedCertificate = new X509Certificate2(stream.ToArray(), password, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);

This trow an exception "unable to decode certicate"

  1. Full code

`using System; using System.Collections.Generic; using System.IO; using System.Linq; using FO_Framework; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cms; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.X509.Extension; using B = Org.BouncyCastle; using System.Security.Cryptography.X509Certificates;

namespace XamarinFormLib.Framework { public class BugX509Certificate { private const string FriendlyName = "Certificat A";

    public void Run()
    {
        String CN = "1234567890";
        String O = "ABC-9999-ABC";
        String OU = "1234567890AB1234";

        String SN = FriendlyName;

        String GN = "AA9999";
        String L = TimeZoneInfo.Local.BaseUtcOffset.ToString().Substring(0, 6);
        String S = "ON";
        String C = "CA";

        GenerateCertificate(CN, O, SN, OU, GN, L, S, C);

    }
    private X509Certificate2 GenerateCertificate(string CN, string O, string SN, string OU, string GN, string L, string S, string C)
    {

        //ECDSA_P256
        var kpg = GeneratorUtilities.GetKeyPairGenerator("ECDSA");

        var secureRandom = new SecureRandom();
        kpg.Init(new KeyGenerationParameters(secureRandom, 256));
        var keys = kpg.GenerateKeyPair();

        var attributes = new Dictionary<DerObjectIdentifier, string>
                         {
                            { X509Name.CN, CN },
                            { X509Name.O, O },
                            { X509Name.Surname, SN },
                            { X509Name.OU, OU },
                            { X509Name.GivenName, GN },
                            { X509Name.L, L },
                            { X509Name.ST, S },
                            { X509Name.C, C }
                        };

        B.X509.X509V3CertificateGenerator extgen2 = new B.X509.X509V3CertificateGenerator();
        var signatureFactory = new Asn1SignatureFactory("SHA256WithECDSA", keys.Private, secureRandom);
        extgen2.SetPublicKey(keys.Public);
        extgen2.SetIssuerDN(new X509Name(attributes.Keys.ToList(), attributes));
        extgen2.AddExtension(X509Extensions.BasicConstraints, false, new BasicConstraints(false));
        var extendedKeyUsage2 = new ExtendedKeyUsage(new[] { new DerObjectIdentifier("1.3.6.1.5.5.7.3.8") });
        extgen2.AddExtension(X509Extensions.ExtendedKeyUsage, true, extendedKeyUsage2.ToAsn1Object());

        extgen2.AddExtension(X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.NonRepudiation));
        extgen2.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifier(new DerOctetString(new SubjectKeyIdentifierStructure(keys.Public))));

        extgen2.SetSerialNumber(GenerateSerialNumber(secureRandom));
        extgen2.SetNotBefore(DateTime.UtcNow.AddDays(-1));
        extgen2.SetNotAfter(DateTime.UtcNow.AddYears(3));
        extgen2.SetSubjectDN(new X509Name(attributes.Keys.ToList(), attributes));

        var certificate = extgen2.Generate(signatureFactory);

        try
        {
            //Convert BouncyCastle certificate with private key to MS System.Security X509Certificate2
            X509Certificate2 x509Certificate2 = ConvertCertificate(certificate,
                                                    keys,
                                                    secureRandom);
            return x509Certificate2;
        }
        catch (Exception e)
        {

            Console.WriteLine(e.Message);
        }

        return null;

    }

    private BigInteger GenerateSerialNumber(SecureRandom random)
    {
        var timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();

        var serialNumber =
            BigIntegers.CreateRandomInRange(
                BigInteger.ValueOf(timestamp), BigInteger.ValueOf(Int64.MaxValue), random);
        return serialNumber;
    }

    private X509Certificate2 ConvertCertificate(B.X509.X509Certificate certificate,
                                                B.Crypto.AsymmetricCipherKeyPair subjectKeyPair,
                                                B.Security.SecureRandom random)
    {
        var store = new Pkcs12Store();

        string friendlyName = FriendlyName; // certificate.SubjectDN.ToString();

        // Add the certificate.
        var certificateEntry = new X509CertificateEntry(certificate);
        store.SetCertificateEntry(friendlyName, certificateEntry);

        // Add the private key.
        store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });

        const string password = "password";
        var stream = new MemoryStream();
        store.Save(stream, password.ToCharArray(), random);

        var convertedCertificate =
            new X509Certificate2(stream.ToArray(),
                                 password,
                                 X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
        return convertedCertificate;
    }
}

}

`

Running the code

var bugTest = new BugX509Certificate(); bugTest.Run();

Expected Behavior

Should create an object of X509Certificate2 no error Same code works on Windows.

Actual Behavior

Throw exception Exception message: "Unable to decode certificate." {System.Security.Cryptography.CryptographicException: Unable to decode certificate. at System.Security.Cryptography.X509Certificates.X509Certificate2ImplMono..ctor (System.Byte[] rawData, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Securit…}

Basic Information

Environment

Show/Hide Visual Studio info ``` Visual Studio Community 2022 for Mac Version 17.5.4 (build 8) Installation UUID: f1242899-4c62-4c3d-8f47-186cabba6e4f Runtime .NET 7.0.1 (64-bit) Architecture: X64 Microsoft.macOS.Sdk 12.3.2372; git-rev-head:754abbf6a3563f6267e5717ae832b4ac25b1f2fb; git-branch:release/7.0.1xx-xcode13.3 Roslyn (Language Service) 5.5.0-3.23056.2+97881342e427ff5cdcba8f12b12ff8e6f3564431 NuGet Version: 6.4.0.117 .NET SDK (x64) SDK: /usr/local/share/dotnet/sdk/7.0.203/Sdks SDK Versions: 7.0.203 6.0.408 6.0.301 5.0.101 3.1.410 3.1.200 2.1.701 2.1.700 2.1.505 MSBuild SDKs: /Applications/Visual Studio.app/Contents/MonoBundle/MSBuild/Current/bin/Sdks .NET Runtime (x64) Runtime: /usr/local/share/dotnet/dotnet Runtime Versions: 7.0.5 7.0.16 5.0.7 5.0.1 3.1.16 3.1.2 2.1.23 2.1.17 2.1.16 2.1.12 2.1.11 2.1.9 Xamarin.Profiler Version: 1.8.0.49 Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler Updater Version: 11 Apple Developer Tools Xcode: 14.2 21534 Build: 14C18 Xamarin.Mac Version: 9.1.0.5 Visual Studio Community Hash: 7738c90c9 Branch: xcode14.2 Build date: 2023-01-25 15:56:14-0500 Xamarin.iOS Version: 16.2.0.5 Visual Studio Community Hash: 7738c90c9 Branch: xcode14.2 Build date: 2023-01-25 15:56:15-0500 Xamarin Designer Version: 17.5.3.47 Hash: e8b5d371c3 Branch: remotes/origin/d17-5 Build date: 2023-04-05 15:57:59 UTC Xamarin.Android Version: 13.2.0.0 (Visual Studio Community) Commit: xamarin-android/d17-5/797e2e1 Android SDK: /Users/vassoume/Library/Developer/Xamarin/android-sdk-macosx Supported Android versions: 8.0 (API level 26) 8.1 (API level 27) 10.0 (API level 29) 8.1 (API level 25) 9.0 (API level 28) 13.0 (API level 33) SDK Command-line Tools Version: 7.0 SDK Platform Tools Version: 33.0.3 SDK Build Tools Version: 32.0.0 Build Information: Mono: 6dd9def Java.Interop: xamarin/java.interop/main@149d70fe SQLite: xamarin/sqlite/3.40.0@fdc1e34 Xamarin.Android Tools: xamarin/xamarin-android-tools/main@9f02d77 Microsoft Build of OpenJDK Java SDK: /Library/Java/JavaVirtualMachines/microsoft-11.jdk 11.0.16.1 Android Designer EPL code available here: https://github.com/xamarin/AndroidDesigner.EPL Eclipse Temurin JDK Java SDK: /Library/Java/JavaVirtualMachines/temurin-8.jdk 1.8.0.302 Android Designer EPL code available here: https://github.com/xamarin/AndroidDesigner.EPL Android SDK Manager Version: 17.5.0.33 Hash: f0c0c52 Branch: remotes/origin/d17-5~2 Build date: 2023-04-05 15:58:04 UTC Android Device Manager Version: 0.0.0.1245 Hash: 7f8a990 Branch: 7f8a990 Build date: 2023-04-05 15:58:04 UTC Build Information Release ID: 1705040008 Git revision: 9a2f0e1a7e2107e6b1174c241a1ca232cde57c49 Build date: 2023-04-05 15:56:22+00 Build branch: release-17.5 Build lane: release-17.5 Operating System Mac OS X 13.4.0 Darwin 22.5.0 Darwin Kernel Version 22.5.0 Mon Apr 24 20:51:50 PDT 2023 root:xnu-8796.121.2~5/RELEASE_X86_64 x86_64 ```

Build Logs

Screenshots

Reproduction Link

Workaround

jfversluis commented 1 year ago

If this is a bug in Mono then you probably want to see if there is an issue already on their repo or report it there. This is not specific to Xamarin.Forms.