如何将在 Android 上创建的 RSA public 密钥成功导入到 C# .net 中

How to import an RSA public key created on Android into C# .net successfully

我正在开发一个使用 public/private 密钥加密、签名和验证 iOS 和 Android 的 Xamarin 移动应用程序。在 iOS 和 Android 上创建的 public 密钥被发送到 .net 服务端点,该服务尝试导入 public 密钥以验证签名。

在 Xamarin.iOS 中,我正在创建 public 密钥,如下所示

public async Task<string> CreateRandomKeyPair()
        {
            using (var access = new SecAccessControl(SecAccessible.WhenUnlockedThisDeviceOnly, SecAccessControlCreateFlags.BiometryAny))
            {
                var keyParameters = new SecKeyGenerationParameters
                {
                    KeyType = SecKeyType.RSA,
                    KeySizeInBits = 2048,
                    Label = AppInfo.PackageName,
                    TokenID = SecTokenID.None,
                    PrivateKeyAttrs = new SecKeyParameters
                    {
                        IsPermanent = true,
                        ApplicationTag = NSData.FromString(AppInfo.PackageName, NSStringEncoding.UTF8),
                        AccessControl = access,
                        CanSign = true,
                        CanVerify = true
                    }
                };

                var privateKey = SecKey.CreateRandomKey(keyParameters.Dictionary, out NSError nsError);
                var publicKey = privateKey.GetPublicKey();
                NSData keyData = publicKey.GetExternalRepresentation();
                var publicKeyString = keyData.GetBase64EncodedString(NSDataBase64EncodingOptions.None);
                return publicKeyString;
            }

在 Xamarin.Android 中,我正在创建 public 密钥,如下所示

public async Task<string> CreateRandomKeyPair()
        {
            KeyPairGenerator _keyPairGenerator = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, "AndroidKeyStore");

            _keyPairGenerator.Initialize(new KeyGenParameterSpec.Builder("MyKeys", KeyStorePurpose.Sign | KeyStorePurpose.Verify)
                .SetDigests(KeyProperties.DigestSha256)
                .SetUserAuthenticationRequired(true)
                .SetSignaturePaddings(KeyProperties.SignaturePaddingRsaPkcs1)
                .SetKeySize(2048).Build());
            KeyPair keyPair = _keyPairGenerator.GenerateKeyPair();
            IPublicKey publicKey = keyPair.Public;
            byte[] publicKeyBytes = publicKey.GetEncoded();
            string publicKeyString = Base64.EncodeToString(publicKeyBytes, Base64Flags.NoWrap);
            return publicKeyString;
        }

在 C#.net 控制台应用程序中,我正在尝试导入 public 密钥,如下所示

using System;
using System.Security.Cryptography;

namespace AndroidPubkey
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var rsa = RSA.Create(2048);
                //Android generated public key
                byte[] pubKey = Convert.FromBase64String("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwgF3bLq/hnNHi3sQoB5C/xusj23ruksW74fCwHMNXxJpsghfqMbhu1E8p2NwxjSkXzDW+QI7DYc8aX9FJruKnvDGswaYdK7vvd4GZmxmYUNPRK6i8cNq/o9mwsAVpv4MmjImY0fSZLjAxW0SXhAZ6iFclOlWkyFo5DmvovBiF4i3CrWpdyHYoVEujRQJ7Rq0q9JuE2lCSNDTIPQYxC9Slw18r5PYjgIgHYmDJvkNlr7wGDl3vlbtHPpUUmRJXCMiuXW7RCp8vasA/2f12MpyTzgSCj/gGb4sPUVvYNQ1n4hU6r3kyz22EW3ZUy5RPPjRn5Hu2CXHMHOeJaB3ZkBoTwIDAQAB");
                //iOS generated public key
                //byte[] pubKey = Convert.FromBase64String("MIIBCgKCAQEAttcTCPsrCic/jC2PGYQUZ41JVn0SD54ZMj01zq5ik72mq1UsQKVb2pwj2lk4ZkZ+nCWU9qk2DddQ9jemE5lWlBVgzh0udyLXpVKESq3YP6DAWcFb45rERryEuYm6steQ5voo62grwLyi8uYTNRuSlUCGfKd/x3tcHm6Mx46P4zSoKv2ykpW6MTgbaTm6D/6/NNA7Qis5+4B/g2eAWJT/rZh6VRf895EQhMvRA1dtCaHqmv95tSJjaiIlRohO4WYJ8bBWfI1z66pMCWFvig5D7Git+pv/A8xCyupBxhkiJDfKS42npuhAcaRbt/QIKUiDWssrlwKyqcNfJghHY12BBwIDAQAB");
                rsa.ImportRSAPublicKey(pubKey, out int bytes);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

虽然在 Xamarin.iOS 上生成的 public 密钥成功导入 到 C#.net 并且我能够执行验证签名,public 在 Xamarin.Android 上生成的密钥无法在 C#.net 中导入 并给我异常消息“导入中的格式未知”。具有以下堆栈跟踪

at Interop.AppleCrypto.ImportEphemeralKey(ReadOnlySpan 1 keyBlob, Boolean hasPrivateKey) at System.Security.Cryptography.RSAImplementation.RSASecurityTransforms.ImportSubjectPublicKeyInfo(ReadOnlySpan 1 source, Int32& bytesRead) at System.Security.Cryptography.RSAImplementation.RSASecurityTransforms.ImportRSAPublicKey(ReadOnlySpan 1 source, Int32& bytesRead) at AndroidPubkey.Program.Main(String[] args) in /Users/developer/Projects/AndroidPubkey/AndroidPubkey/Program.cs:line 17

注意:我是 运行 Mac OS 上的控制台应用程序。我看到在 Android 上创建的 public 密钥字符串比在 iOS 上创建的密钥字符串长。

请帮助我理解这里的问题并创建跨平台兼容的 RSA 密钥。

根据评论中@Topaco 的建议,我可以按以下方式解决问题。我在这里回答,以便其他寻找答案的人可以从中受益。 我已经为 android 添加了 public-key 字节数组长度为 294 的条件,否则它将作为 iOS 生成的 public-key.

导入
using System;
using System.Security.Cryptography;

namespace AndroidPubkey
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var rsa = RSA.Create(2048);
                 //Android generate public key
                byte[] pubKey = Convert.FromBase64String("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgov1p4sCDlCxRaGq4Am3sDOffwtP4iVdmIzXDqELyA251UBBAbZeyC/wCrVllkNAS644nFHDbkPko2EZWIMY0Rl9t/x37GV2gAnJjQ8HLR/WnxqsHiD83qczCqkHwr/AiT4bdAJyiFMwWn20fXfiiksxP0dtmvzYFz0igONj1/PRuzVnblTDIvgP5Okq1EvKC1UjRV8QDTPqzxFbTzi1YAsL02n1zEsnYH3+96syPHi9TE9r9/vk1h/M+q3p+gS0nwpz5Dz7+DQAvOYMkCsNU/IjU6GZwQNwMfh2s59UuYFppckj9gI8LGlBq2QVCAZj9U9LEGawj4BggOGmiRPXFQIDAQAB");
                //iOS generated public key
                //byte[] pubKey = Convert.FromBase64String("MIIBCgKCAQEAttcTCPsrCic/jC2PGYQUZ41JVn0SD54ZMj01zq5ik72mq1UsQKVb2pwj2lk4ZkZ+nCWU9qk2DddQ9jemE5lWlBVgzh0udyLXpVKESq3YP6DAWcFb45rERryEuYm6steQ5voo62grwLyi8uYTNRuSlUCGfKd/x3tcHm6Mx46P4zSoKv2ykpW6MTgbaTm6D/6/NNA7Qis5+4B/g2eAWJT/rZh6VRf895EQhMvRA1dtCaHqmv95tSJjaiIlRohO4WYJ8bBWfI1z66pMCWFvig5D7Git+pv/A8xCyupBxhkiJDfKS42npuhAcaRbt/QIKUiDWssrlwKyqcNfJghHY12BBwIDAQAB");
                if (pubKey.Length == 294)
                {
                    rsa.ImportSubjectPublicKeyInfo(pubKey, out int bytes);
                }
                else
                {
                    rsa.ImportRSAPublicKey(pubKey, out int bytes);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}