我不是来自安全背景,而是继承了以下代码。
我希望能够将其移植到.NET Standard中使用。
public sealed class Pem
{
private string type;
private string base64Encoded;
private const string PemStart = "-----BEGIN ";
private const string PemEnd = "-----END ";
private const string PemEndOfLine = "-----";
public Pem(string content)
{
using (var reader = new StringReader(content.Trim()))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith(PemStart) && line.EndsWith(PemEndOfLine))
{
type = line.Substring(PemStart.Length, line.Length - PemStart.Length - PemEndOfLine.Length);
}
else if (line.StartsWith(PemEnd))
{
//ignore
}
else
{
base64Encoded += line;
}
}
}
}
public string Type
{
get { return type; }
}
public byte[] Decoded
{
get { return Convert.FromBase64String(base64Encoded); }
}
}
public class EncryptionService : IEncryption
{
public string Encrypt(string target)
{
var key = "-----BEGIN PUBLIC KEY-----\nXXXXKEYMATERIALREMOVEDXXXX\n-----END PUBLIC KEY-----";
var pem = new Pem(key);
byte[] keyMaterial = pem.Decoded;
byte[] data = Encoding.UTF8.GetBytes(target);
var provider = PCLCrypto.WinRTCrypto.AsymmetricKeyAlgorithmProvider.OpenAlgorithm(PCLCrypto.AsymmetricAlgorithm.RsaPkcs1);
var publicKey = provider.ImportPublicKey(keyMaterial);
byte[] cipherText = PCLCrypto.WinRTCrypto.CryptographicEngine.Encrypt(publicKey, data);
return Convert.ToBase64String(cipherText);
}
}
}
任何人都可以指出我如何使用
重写.NET Standard 2.0的正确方向假设密钥保持字符串格式,如代码所示。
答案 0 :(得分:1)
将您的任务分成两部分:
您可能需要尝试以下代码来执行第一阶段。
我用它将PEM证书解码为X509Certificate2类。
public class Crypto
{
/// <summary>
/// This helper function parses an RSA private key using the ASN.1 format
/// </summary>
/// <param name="privateKeyBytes">Byte array containing PEM string of private key.</param>
/// <returns>An instance of <see cref="RSACryptoServiceProvider"/> rapresenting the requested private key.
/// Null if method fails on retriving the key.</returns>
public static RSACryptoServiceProvider DecodeRsaPrivateKey(byte[] privateKeyBytes)
{
MemoryStream ms = new MemoryStream(privateKeyBytes);
BinaryReader rd = new BinaryReader(ms);
try
{
byte byteValue;
ushort shortValue;
shortValue = rd.ReadUInt16();
switch (shortValue)
{
case 0x8130:
// If true, data is little endian since the proper logical seq is 0x30 0x81
rd.ReadByte(); //advance 1 byte
break;
case 0x8230:
rd.ReadInt16(); //advance 2 bytes
break;
default:
Debug.Assert(false); // Improper ASN.1 format
return null;
}
shortValue = rd.ReadUInt16();
if (shortValue != 0x0102) // (version number)
{
Debug.Assert(false); // Improper ASN.1 format, unexpected version number
return null;
}
byteValue = rd.ReadByte();
if (byteValue != 0x00)
{
Debug.Assert(false); // Improper ASN.1 format
return null;
}
// The data following the version will be the ASN.1 data itself, which in our case
// are a sequence of integers.
// In order to solve a problem with instancing RSACryptoServiceProvider
// via default constructor on .net 4.0 this is a hack
CspParameters parms = new CspParameters();
parms.Flags = CspProviderFlags.NoFlags;
parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant();
parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms);
RSAParameters rsAparams = new RSAParameters();
rsAparams.Modulus = rd.ReadBytes(Helpers.DecodeIntegerSize(rd));
// Argh, this is a pain. From emperical testing it appears to be that RSAParameters doesn't like byte buffers that
// have their leading zeros removed. The RFC doesn't address this area that I can see, so it's hard to say that this
// is a bug, but it sure would be helpful if it allowed that. So, there's some extra code here that knows what the
// sizes of the various components are supposed to be. Using these sizes we can ensure the buffer sizes are exactly
// what the RSAParameters expect. Thanks, Microsoft.
RSAParameterTraits traits = new RSAParameterTraits(rsAparams.Modulus.Length * 8);
rsAparams.Modulus = Helpers.AlignBytes(rsAparams.Modulus, traits.size_Mod);
rsAparams.Exponent = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Exp);
rsAparams.D = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_D);
rsAparams.P = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_P);
rsAparams.Q = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Q);
rsAparams.DP = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DP);
rsAparams.DQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DQ);
rsAparams.InverseQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_InvQ);
rsa.ImportParameters(rsAparams);
return rsa;
}
catch (Exception)
{
Debug.Assert(false);
return null;
}
finally
{
rd.Close();
}
}
}
public class Helpers
{
/// <summary>
/// This helper function parses an integer size from the reader using the ASN.1 format
/// </summary>
/// <param name="rd"></param>
/// <returns></returns>
public static int DecodeIntegerSize(System.IO.BinaryReader rd)
{
byte byteValue;
int count;
byteValue = rd.ReadByte();
if (byteValue != 0x02) // indicates an ASN.1 integer value follows
return 0;
byteValue = rd.ReadByte();
if (byteValue == 0x81)
{
count = rd.ReadByte(); // data size is the following byte
}
else if (byteValue == 0x82)
{
byte hi = rd.ReadByte(); // data size in next 2 bytes
byte lo = rd.ReadByte();
count = BitConverter.ToUInt16(new[] { lo, hi }, 0);
}
else
{
count = byteValue; // we already have the data size
}
//remove high order zeros in data
while (rd.ReadByte() == 0x00)
{
count -= 1;
}
rd.BaseStream.Seek(-1, System.IO.SeekOrigin.Current);
return count;
}
/// <summary>
///
/// </summary>
/// <param name="pemString"></param>
/// <param name="type"></param>
/// <returns></returns>
public static byte[] GetBytesFromPEM(string pemString, PemStringType type)
{
string header; string footer;
switch (type)
{
case PemStringType.Certificate:
header = "-----BEGIN CERTIFICATE-----";
footer = "-----END CERTIFICATE-----";
break;
case PemStringType.RsaPrivateKey:
header = "-----BEGIN RSA PRIVATE KEY-----";
footer = "-----END RSA PRIVATE KEY-----";
break;
default:
return null;
}
int start = pemString.IndexOf(header) + header.Length;
int end = pemString.IndexOf(footer, start) - start;
return Convert.FromBase64String(pemString.Substring(start, end));
}
/// <summary>
///
/// </summary>
/// <param name="inputBytes"></param>
/// <param name="alignSize"></param>
/// <returns></returns>
public static byte[] AlignBytes(byte[] inputBytes, int alignSize)
{
int inputBytesSize = inputBytes.Length;
if ((alignSize != -1) && (inputBytesSize < alignSize))
{
byte[] buf = new byte[alignSize];
for (int i = 0; i < inputBytesSize; ++i)
{
buf[i + (alignSize - inputBytesSize)] = inputBytes[i];
}
return buf;
}
else
{
return inputBytes; // Already aligned, or doesn't need alignment
}
}
}
public enum PemStringType
{
Certificate,
RsaPrivateKey
}
internal class RSAParameterTraits
{
public RSAParameterTraits(int modulusLengthInBits)
{
// The modulus length is supposed to be one of the common lengths, which is the commonly referred to strength of the key,
// like 1024 bit, 2048 bit, etc. It might be a few bits off though, since if the modulus has leading zeros it could show
// up as 1016 bits or something like that.
int assumedLength = -1;
double logbase = Math.Log(modulusLengthInBits, 2);
if (logbase == (int)logbase)
{
// It's already an even power of 2
assumedLength = modulusLengthInBits;
}
else
{
// It's not an even power of 2, so round it up to the nearest power of 2.
assumedLength = (int)(logbase + 1.0);
assumedLength = (int)(Math.Pow(2, assumedLength));
System.Diagnostics.Debug.Assert(false); // Can this really happen in the field? I've never seen it, so if it happens
// you should verify that this really does the 'right' thing!
}
switch (assumedLength)
{
case 1024:
this.size_Mod = 0x80;
this.size_Exp = -1;
this.size_D = 0x80;
this.size_P = 0x40;
this.size_Q = 0x40;
this.size_DP = 0x40;
this.size_DQ = 0x40;
this.size_InvQ = 0x40;
break;
case 2048:
this.size_Mod = 0x100;
this.size_Exp = -1;
this.size_D = 0x100;
this.size_P = 0x80;
this.size_Q = 0x80;
this.size_DP = 0x80;
this.size_DQ = 0x80;
this.size_InvQ = 0x80;
break;
case 4096:
this.size_Mod = 0x200;
this.size_Exp = -1;
this.size_D = 0x200;
this.size_P = 0x100;
this.size_Q = 0x100;
this.size_DP = 0x100;
this.size_DQ = 0x100;
this.size_InvQ = 0x100;
break;
default:
System.Diagnostics.Debug.Assert(false); // Unknown key size?
break;
}
}
public int size_Mod = -1;
public int size_Exp = -1;
public int size_D = -1;
public int size_P = -1;
public int size_Q = -1;
public int size_DP = -1;
public int size_DQ = -1;
public int size_InvQ = -1;
}
最后,您需要从您的程序中调用它:
public X509Certificate2 GetCertificateFromText(string pem, string key)
{
byte[] certBuffer, keyBuffer;
if (string.IsNullOrEmpty(pem))
{
throw new ArgumentNullException("Missing certificate");
}
try
{
certBuffer = Helpers.GetBytesFromPEM(pem, PemStringType.Certificate);
keyBuffer = Helpers.GetBytesFromPEM(pem, PemStringType.RsaPrivateKey);
X509Certificate2 certificate = new X509Certificate2(certBuffer, "your_key_password");
RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
certificate.PrivateKey = prov;
return certificate;
}
catch (Exception ex)
{
throw new ArgumentException("Invalid certificate", ex);
}
}
我在csharpcorner的某个地方找到了代码,但是现在还无法找到原始链接。
答案 1 :(得分:0)
对不起,我们没有机会查看@komsky发布的上述代码,但作为参考,我们最终还是使用了此答案引用的PemKeyUtils类-
How to load the RSA public key from file in C#
如下-
public class EncryptionService : Interfaces.IEncryption
{
public string Encrypt(string target)
{
var key = "-----BEGIN PUBLIC KEY-----\YOURKEY\n-----END PUBLIC KEY-----";
var rsaProvider = PemKeyUtils.GetRSAProviderFromPemString(key);
RSAEncyptionManager manager = new RSAEncyptionManager(rsaProvider, RSAEncryptionPadding.Pkcs1);
return Convert.ToBase64String(manager.Encrypt(target));
}
}
public class RSAEncyptionManager {
private readonly RSACryptoServiceProvider _serviceProvider;
private readonly RSAEncryptionPadding _padding;
private RSAParameters _rSAKeyInfo;
public RSAEncyptionManager(RSACryptoServiceProvider serviceProvider, RSAEncryptionPadding padding)
{
_serviceProvider = serviceProvider;
_padding = padding;
_rSAKeyInfo = _serviceProvider.ExportParameters(false);
}
public byte[] Encrypt(string stringToEncrypt)
{
return _serviceProvider.Encrypt(Encoding.UTF8.GetBytes(stringToEncrypt), _padding);
}
public RSAEncyptionManager SetKey(byte[] key) {
_rSAKeyInfo.Modulus = key;
_serviceProvider.ImportParameters(_rSAKeyInfo);
return this ;
}
}