Search code examples
androidmauivisual-studio-2022.net-9.0

Loosing XML cryptographic tooling in android release mode of a MAUI hybrid app


I have a Blazor MAUI Hybrid app in .NET 9. I also have a library in that solution doing some XML signature verification. I have a null exception, but only in release mode on android.

The code is as follows:

public static ConfigRoot ValidateAndLoadSignedConfig(string signedXml, RSA publicKey)
{
    // Load the signed XML
    XmlDocument xmlDoc = new();
    xmlDoc.LoadXml(signedXml);

    // Verify the signature
    SignedXml signedXmlVerifier = new(xmlDoc);
    XmlNodeList signatureNodes = xmlDoc.GetElementsByTagName("Signature");
    
    if (signatureNodes?.Count != 1)
    {
        throw new CryptographicException("No signature");
    }

    signedXmlVerifier.LoadXml((XmlElement)signatureNodes[0]);

    if (!signedXmlVerifier.CheckSignature(publicKey)) // <<<= this row throws the exception
    {
        throw new CryptographicException("XML compromised!");
    }

    // Deserialize the validated XML to ConfigRoot
    XmlSerializer serializer = new(typeof(ConfigRoot));
    using StringReader reader = new(xmlDoc.OuterXml);

    return (ConfigRoot)serializer.Deserialize(reader);
}

The stack trace of the exception is this:

enter image description here

It works fine in debug mode both on the device and emulator, and I have no issues on Windows either, only on Android.

The RSA public key is correctly populated form an XML, the signed XML is loaded and the signature node is also correct.

I think something is optimized away. So I have added these to my application project's csproj:

<ItemGroup>
  <TrimmerRootAssembly Include="System.Security.Cryptography.Xml" RootMode="All" />
  <TrimmerRootAssembly Include="System.Security.Cryptography.Pkcs" RootMode="All" />
</ItemGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net9.0-android|AnyCPU'">
    <AndroidPackageFormat>apk</AndroidPackageFormat>
    <AndroidEnableR8>true</AndroidEnableR8>
    <AndroidLinkTool>r8</AndroidLinkTool>
    <AndroidProguardConfig>proguard.cfg</AndroidProguardConfig>
    <DebugType>portable</DebugType>
    <DebugSymbols>true</DebugSymbols>
    <AndroidLinkMode>SdkOnly</AndroidLinkMode>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net9.0-android'">
  <ProguardConfiguration Include="Platforms\Android\proguard.cfg" />
</ItemGroup>

With this proguard.cfg (I have to admit, I have no clue what I am doing here related to the android linker and optimizer. All of it was suggested by AI and/or found online - make sense, but no difference):

    -keep class androidx.security.crypto.** { *; }
    -keep class com.google.crypto.tink.** { *; }
    
    # Keep all classes in System.Security.Cryptography.Xml
    -keep class System.Security.Cryptography.Xml.** { *; }
    
    # Keep classes used by SignedXml and RSA
    -keep class System.Security.Cryptography.Xml.SignedXml { *; }
    -keep class System.Security.Cryptography.Xml.RSAPKCS1SignatureDescription { *; }
    
    # Keep XML serialization classes (if used)
    -keep class System.Xml.** { *; }

But it makes no difference...

[Solved]

Right after posting this I found this one on github: https://github.com/dotnet/runtime/issues/97242

It was promisingly similar, hence I have added System.Security.Cryptography to my TrimRootAssembly list, and it worked. I might fine-tune later with the TrimmerRootDescriptor, but for now, I am happy with what I have. I have also removed all the android linker related stuff.


Solution

  • I found this one on github: https://github.com/dotnet/runtime/issues/97242

    It was promisingly similar, hence I have added System.Security.Cryptography to my TrimRootAssembly list, and it worked. I might fine-tune later with the TrimmerRootDescriptor, but for now, I am happy with what I have. I have also removed all the android linker related stuff.