如何将在 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);
}
}
}
}
我正在开发一个使用 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);
}
}
}
}