Search code examples
androidxamarinxamarin.androidbouncycastlespongycastle

Xamarin.Android binding Spongy Castle / Bouncy Castle


Anyone successfully bound SpongyCastle to Xamarin.Android? I bump into a bunch of warnings with my Metadata.xml in the binding project.

So far I have:

<remove-node path="/api/package[@name='org.spongycastle.x509']" />
<remove-node path="/api/package[@name='org.spongycastle.crypto']" />
<remove-node path="/api/package[@name='org.spongycastle.crypto.tls']" />
<remove-node path="/api/package[@name='org.spongycastle.cms']" />
<remove-node path="/api/package[@name='org.spongycastle.crypto.prng']" />
<remove-node path="/api/package[@name='org.spongycastle.openpgp']" />
<remove-node path="/api/package[@name='org.spongycastle.openssl']" />
<remove-node path="/api/package[@name='org.spongycastle.cert.ocsp']" />

<remove-node path="/api/package[@name='org.spongycastle.jcajce']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.dh']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.ec']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.digest']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.keystore.bc']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.symmetric']" />

<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.dsa']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.util']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.symmetric.util']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.gost']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.ies']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.rsa']" />
<remove-node path="/api/package[@name='org.spongycastle.jcajce.provider.asymmetric.x509']" />

<remove-node path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='CertStoreCollectionSpi']" />
<remove-node path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='MultiCertStoreSpi']" />
<remove-node path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='X509CRLEntryObject']" />
<remove-node path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='X509CRLObject']" />
<remove-node path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='X509CertificateObject']" />
<remove-node path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='X509LDAPCertStoreSpi']"/>
<remove-node path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='PKIXPolicyNode']" />

<remove-node path="/api/package[@name='org.spongycastle.pqc.jcajce.provider.rainbow']" />
<remove-node path="/api/package[@name='org.spongycastle.pqc.jcajce.provider.mceliece']"/>
<remove-node path="/api/package[@name='org.spongycastle.pqc.jcajce.provider.util']" />
<remove-node path="/api/package[@name='org.spongycastle.pqc.crypto.ntru']" />
<remove-node path="/api/package[@name='org.spongycastle.pqc.math.ntru.polynomial']" />

So it compiles, but when using the binding project in the Xamarin.Android project it takes several mins. to compile and then it fails complaining about the HEAP size of Java.

When I set the heap size to 1GB, it completes, but debugging is then broken when running the app in debug mode on device.

Is there a way to just use the ARRs without a binding library? I just need to invoke a wrapper method that I made in this ARR and get the output from that. I don't need to access the full library through C#. Or is there a better way?

Update: And when I build the CPU looks like this (Look at Java): enter image description here


Solution

  • I ended up removing almost everything related to BouncyCastle and SpongyCastle in my metadata.xml file in the binding project. Then I copied parts of the generated BouncyCastleProvider from the generated files folder in the Binding Project. So I created my own JNI wrapper only for the parts I needed to call.

    It works great. Now compile time is reduced to a few seconds and deployment is fast during debugging. And I enjoy the third party libraries.

    My take from this was to generate C# classes first and then choose to implement parts of the JNI in my own library and add/remove nodes in Metadata.xml.

    using System;
    using System.Collections.Generic;
    using Android.Runtime;
    namespace Org.Spongycastle.Jce.Provider
    {
    
    // Metadata.xml XPath class reference: path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='BouncyCastleProvider']"
    [global::Android.Runtime.Register("org/spongycastle/jce/provider/BouncyCastleProvider", DoNotGenerateAcw = true)]
    public sealed partial class BouncyCastleProvider : global::Java.Security.Provider
    {
        // Metadata.xml XPath field reference: path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='BouncyCastleProvider']/field[@name='PROVIDER_NAME']"
        [Register("PROVIDER_NAME")]
        public const string ProviderName = (string)"SC";
    
        internal static IntPtr java_class_handle;
        internal static IntPtr class_ref
        {
            get
            {
                return JNIEnv.FindClass("org/spongycastle/jce/provider/BouncyCastleProvider", ref java_class_handle);
            }
        }
    
        protected override IntPtr ThresholdClass
        {
            get { return class_ref; }
        }
    
        protected override global::System.Type ThresholdType
        {
            get { return typeof(BouncyCastleProvider); }
        }
    
        internal BouncyCastleProvider(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { }
    
        static IntPtr id_ctor;
        // Metadata.xml XPath constructor reference: path="/api/package[@name='org.spongycastle.jce.provider']/class[@name='BouncyCastleProvider']/constructor[@name='BouncyCastleProvider' and count(parameter)=0]"
        [Register(".ctor", "()V", "")]
        public unsafe BouncyCastleProvider()
            : base(IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
        {
            if (Handle != IntPtr.Zero)
                return;
    
            try
            {
                if (GetType() != typeof(BouncyCastleProvider))
                {
                    SetHandle(
                            global::Android.Runtime.JNIEnv.StartCreateInstance(GetType(), "()V"),
                            JniHandleOwnership.TransferLocalRef);
                    global::Android.Runtime.JNIEnv.FinishCreateInstance(Handle, "()V");
                    return;
                }
    
                if (id_ctor == IntPtr.Zero)
                    id_ctor = JNIEnv.GetMethodID(class_ref, "<init>", "()V");
                SetHandle(
                        global::Android.Runtime.JNIEnv.StartCreateInstance(class_ref, id_ctor),
                        JniHandleOwnership.TransferLocalRef);
                JNIEnv.FinishCreateInstance(Handle, class_ref, id_ctor);
            }
            finally
            {
            }
        }
    }
    }