<RSAKeyValue><Modulus>D4ZblrI8NsZgnZOQbHJuEv7Lg0pCOuhwmGIS10rwlJ9Z/hGbes=</Modulus><Exponent>ABCD</Exponent><P>86TWhp553PrrlQ/xdOpGDbGy8/h3/5vg+P==</P><Q>D33zQmGtBnoO8OaQhCwbwqai1w==</Q><DP>i0RwPcLPh+WQKt5L75SR1PQ==</DP><DQ>3vayrPOJ1WMFxPzCpqMaUDWwWsBJ/c0PRwRhvrSw4/TR+6BlEkU/5b17/==</DQ><InverseQ>CbuGb8ZNvjCkSHqvSTJvFpFfqxxQjH09ABxBZx3K7SNvw3+6+LfKeRzoz7==</InverseQ><D>p6etDZ1ghROHdHWAauTUe6zn/RQzm7HE5aCWVOgMqcg9VkFMMZ3H+R1rec=</D></RSAKeyValue>

上面你可以看到我的Public Key.xml文件的示例格式,我需要将它转换成pem格式进行rsa加密,主要需要加密用户名和密码.

import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:xml/xml.dart' as xml;

void encryptRSa() {
  String publicKeyXML = '''
   <RSAKeyValue><Modulus>D4ZblrI8NsZgnZOQbHJuEv7Lg0pCOuhwmGIS10rwlJ9Z/hGbes=</Modulus><Exponent>ABCD</Exponent><P>86TWhp553PrrlQ/xdOpGDbGy8/h3/5vg+P==</P><Q>D33zQmGtBnoO8OaQhCwbwqai1w==</Q><DP>i0RwPcLPh+WQKt5L75SR1PQ==</DP><DQ>3vayrPOJ1WMFxPzCpqMaUDWwWsBJ/c0PRwRhvrSw4/TR+6BlEkU/5b17/==</DQ><InverseQ>CbuGb8ZNvjCkSHqvSTJvFpFfqxxQjH09ABxBZx3K7SNvw3+6+LfKeRzoz7==</InverseQ><D>p6etDZ1ghROHdHWAauTUe6zn/RQzm7HE5aCWVOgMqcg9VkFMMZ3H+R1rec=</D></RSAKeyValue>
  ''';

  var document = xml.XmlDocument.parse(publicKeyXML);
  var modulusBase64 = document.findAllElements('Modulus').single.text;
  var exponentBase64 = document.findAllElements('Exponent').single.text;

  var modulusBytes = Uint8List.fromList(base64.decode(modulusBase64));
  var exponentBytes = Uint8List.fromList(base64.decode(exponentBase64));

  var modulus = decodeBigIntFromBytes(modulusBytes);
  var exponent = decodeBigIntFromBytes(exponentBytes);

  var publicKey = RSAPublicKey(modulus, exponent);

  var encryptor = OAEPEncoding(RSAEngine())
    ..init(true, PublicKeyParameter<RSAPublicKey>(publicKey));

  String message = 'Hello, World!';

  var encrypted = encryptor.process(Uint8List.fromList(utf8.encode(message)));

  print('EncryptedOne: ${base64.encode(encrypted)}');
}

BigInt decodeBigIntFromBytes(Uint8List bytes) {
  return BigInt.parse(base64.encode(bytes), radix: 16);
}

我正在使用PointCastle&amp;XML库进行加密,当我运行这段代码时,我得到了这个问题 FormatException:无法分析BigInt

public static string EncryptUserNamePassword(string value, string publicKey)

        {

            if (!(String.IsNullOrEmpty(value)))

            {

                using (var rsa = new RSACryptoServiceProvider())

                {

                    try

                    {

                        rsa.FromXmlString(publicKey);

                        byte[] dataToEncrypt = Encoding.ASCII.GetBytes(value);

                        byte[] encryptedByteArray = rsa.Encrypt(dataToEncrypt, true).ToArray();

                        return Convert.ToBase64String(encryptedByteArray);

 

                    }

                    catch (Exception ex) { }

                    finally

                    {

                        rsa.PersistKeyInCsp = false;

                    }

                }

            }

            return "";

        }




public static string Decrypt(string value, string _privateKey, IConfiguration configuration)

        {

            if (!(String.IsNullOrEmpty(value)))

            {

                var keyFilePath = configuration.GetSection("KeyPath").GetValue<string>(_privateKey);

                var privateKey = File.ReadAllText(keyFilePath);

 

                using (var rsa = new RSACryptoServiceProvider())

                {

                    try

                    {

                        rsa.FromXmlString(privateKey);

                        var dataByte = Convert.FromBase64String(value.Replace(" ", ""));

                        var decryptedByte = rsa.Decrypt(dataByte, true);

                        var decryptedData = Encoding.UTF8.GetString(decryptedByte);

                        return decryptedData;

                    }

                    catch (Exception ex) { }

                    finally

                    {

                        rsa.PersistKeyInCsp = false;

                    }

                }

            }

            return "";

 

        }

上面附上了服务器端使用的加解密代码,它是一个C#代码.

推荐答案

错误在于,在函数decodeBigIntFromBytes()中,BigInt.parse()中的数据被错误地进行了Base64编码.正确是十六进制编码,与您使用的基数16相对应:

import 'package:convert/convert.dart';
...
BigInt decodeBigIntFromBytes(Uint8List bytes) {
  return BigInt.parse(hex.encode(bytes), radix: 16);
}

通过这种更改,代码可以正常工作,包括公钥的导入和加密本身.


附加的,但次要的:应该使用innerText代替过期的text,例如:

var modulusBase64 = document.findAllElements('Modulus').single.innerText;

如果您想要导出/导入PEM格式的密钥,basic_utils包提供了合适的功能,例如可以使用CryptoUtils.encodeRSAPublicKeyToPem()来导出X.509/SPKI格式的公钥PEM:

import 'package:basic_utils/basic_utils.dart';
...
print('X.509/SPKI, PEM: ${CryptoUtils.encodeRSAPublicKeyToPem(publicKey)}');

请注意,正如 comments 中已经指出的那样,发布的密钥实际上是一个private RSA密钥.public密钥只包含ModulusExponent字段(所有其他字段都属于私钥,不得公开).然而,由于这显然是一个测试密钥,披露可能并不关键.


使用有效的测试密钥完成代码:

import 'dart:convert';
import 'dart:typed_data';
import 'package:convert/convert.dart';
import 'package:pointycastle/export.dart';
import 'package:xml/xml.dart' as xml;
import 'package:basic_utils/basic_utils.dart';

...

void encryptRSa() {

  // key import
  var publicKeyXML = '''<RSAKeyValue><Modulus>unF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>''';
  var xmlDocument = xml.XmlDocument.parse(publicKeyXML);
  var modulus = getData(xmlDocument, 'Modulus');
  var exponent = getData(xmlDocument, 'Exponent');
  var publicKey = RSAPublicKey(modulus, exponent);

  // encryption
  var message = 'Hello, World!';
  var encryptor = OAEPEncoding(RSAEngine())..init(true, PublicKeyParameter<RSAPublicKey>(publicKey)); // Applies SHA-1 by default for both digests, i.e. the OAEP and MGF1 digest, and an empty label
  var encrypted = encryptor.process(Uint8List.fromList(utf8.encode(message)));
  print('EncryptedOne: ${base64.encode(encrypted)}');

  // key export
  print('X.509/SPKI, PEM: ${CryptoUtils.encodeRSAPublicKeyToPem(publicKey)}');
}

BigInt getData(xml.XmlDocument xmlDocument, String element){
  var dataB64 = xmlDocument.findAllElements(element).single.innerText;
  var dataBytes = Uint8List.fromList(base64.decode(dataB64));
  return BigInt.parse(hex.encode(dataBytes), radix: 16);
}

Edit:关于 comments 中关于导入私钥的问题.私钥的导入是类似的.需要元素ModulusD(私有指数)、P(素数1)和Q(素数2).这些值被用于实例化RSAPrivateKey,其然后可以被应用于解密.

使用有效的私钥(与加密代码中的公钥相关联)完成代码:

...
// key import
var privateKeyXML = '''<RSAKeyValue><Modulus>unF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGw==</Modulus><Exponent>AQAB</Exponent><P>3e+jND6OS6ofGYUN6G4RapHzuRAV8ux1C9eXMOdZFbcBehn/ydhzR48LIPTW9HiRE00um27lXfW5/POCaEUvfOp1UxTWeHZ4xICo40PBo383ZKW1MbES1oiMbjkEqSFGRnTItnLU07bKbzLA7I0UWHWCEAnv0g7HRxk973FAsm8=</P><Q>1w8+olZ2POBYeYgw1a0DkeJWKMQi/4pAgyYwustZo0dHlRXQT0OI9XQ0j1PZWoQS28tFcmoEAg6f5MUDpdM9swS0SOCPI1Lc/f/Slus3u1O3UCezk37pneSPezskDhvV2cClJEYH8m/zwDAUlEi4KLIt/H/jgtyDd6pbxxc78RU=</Q><DP>iE6VAxJknM4oeakBiL6JTdXEReY+RMu7e4F2518/lJmoe5CaTCL3cnzFTgFyQAYIvD0MIgSzNMkl6Ni6QEY1y1fIpTVIIAZLWAzZLXPA6yTIJbWsmo9xzXdiIJQ+a433NnClkYDne/xpSnB2kxJ263mIX0drFq1i8STsqDH7lVs=</DP><DQ>VqUJsxXqpTQt8Sjxo+UE3y21UM9U2me0/iHQ2DE9eA8rw+D6ADVRZLLgyi4aD+HOR0dqP2J/IuUJfn3xrkmhPhLTH9l5Ud38s0jya2NxHMPpwx17uB0Vuktvk1KMgDKuwgBfiHG+meqI5hF4+RUjPSIsbOKJoxt8zCWSvG+b8tE=</DQ><InverseQ>s9Fu1JsTak+C84codMY+vuApuaxZVs5xADysbzTVPfxb9Q97Ve3KcwSPPNDb05pV5DC9Q334PEVcnpi/CPqKHhZ2rXT2Ls6jV8OcxzM5A30MpyHZ40Aes1I4zIsMIGb77BvIcCxLZPRU7z6DMsAG+JmbkAUJBZ+R7gtmjmY5LXQ=</InverseQ><D>SlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODwczdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfyqmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBXmNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyFEM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQ==</D></RSAKeyValue>''';
var xmlDocument = xml.XmlDocument.parse(privateKeyXML);
var modulus = getData(xmlDocument, 'Modulus');
var privateExponent = getData(xmlDocument, 'D');
var p = getData(xmlDocument, 'P');
var q = getData(xmlDocument, 'Q');
var privateKey = RSAPrivateKey(modulus, privateExponent, p, q);

var decryptor = OAEPEncoding(RSAEngine())..init(false, PrivateKeyParameter<RSAPrivateKey>(privateKey)); // Applies SHA-1 by default for both digests, i.e. the OAEP and MGF1 digest, and an empty label
var decrypted = decryptor.process(base64.decode(encryptedBase64)); // encryptedBase64: Base64 encoded ciphertext

print('Decrypted data: ${utf8.decode(decrypted)}');

// key export
print('PKCS#8, PEM: ${CryptoUtils.encodeRSAPrivateKeyToPem(privateKey)}');

Csharp相关问答推荐

.NET框架4.7.2项目如何引用.NET Core 2.2库?

使用C#中的Shape API从Azure目录获取所有用户

在依赖性注入和继承之间进行 Select

错误401未授权ASP.NET Core.使用JWT创建身份验证/授权

C#自定义字典与JSON(de—)serialize

应该使用哪一个?"_counter += 1 OR互锁增量(ref_counter)"""

始终保留数组中的最后N个值,丢弃最老的

Unity中的刚体2D运动

当前的文化决定了错误的文化

如何注册类使用多级继承与接口

Docker Container中的HttpRequest后地址不可用

当前代码Cosmos DB 3.37.1:PartitionKey key key mismatch exception

HttpClient 415不支持的媒体类型错误

当试图限制EF Select 的列时,如何避免重复代码?

VS 2022与VS 2019:如何/为什么创建额外的任务?

源代码生成器项目使用`dotnet build`编译,而不是在Visual Studio中编译?

如何在Xamarin.Forms中检索PanGesture事件的位置?

在C#和HttpClient中使用REST API

是否在异步方法中避免Span<;T>;.ToArray()?

使用';UnityEngineering.Random.Range()';的IF语句仅适用于极高的最大值