Implementação RSA em C #
-
23-08-2019 - |
Pergunta
Atualmente estou tentando implementar uma classe para lidar com comunicações seguras entre instâncias do meu aplicativo usando classe RSACrytoServiceProveider. Primeira pergunta: é uma boa idéia implementar uma única classe para lidar com papéis remetente / Receptor do ou devo dividir as funções em classes individuais?. Isto é o que eu tenho feito até agora:
using System;
using System.Text;
using System.Security.Cryptography;
namespace Agnus.Cipher
{
public class RSA
{
private byte[] plaintextBytes;
private byte[] ciphertextBytes;
private RSACryptoServiceProvider rSAProviderThis;
private RSACryptoServiceProvider rSAProviderOther;
public string PublicKey
{
get { return rSAProviderThis.ToXmlString(false); }
}
public RSA()
{
rSAProviderThis = new RSACryptoServiceProvider { PersistKeyInCsp = true };
plaintextBytes = Encoding.Unicode.GetBytes(PublicKey);
}
public void InitializeRSAProviderOther(string parameters)
{
rSAProviderOther.FromXmlString(parameters);
}
public byte[] Encrypt()
{
return rSAProviderThis.Encrypt(plaintextBytes, true);
}
public byte[] Decrypt()
{
return rSAProviderThis.Decrypt(ciphertextBytes, true);
}
public byte[] Sign()
{
using (SHA1Managed SHA1 = new SHA1Managed())
{
byte[] hash = SHA1.ComputeHash(ciphertextBytes);
byte[] signature = rSAProviderThis.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
return signature;
}
}
public void Verify()
{
throw new NotImplementedException();
}
}
}
Segunda pergunta: como faço para enviar e receber dados a ser alimentado na classe? eu sou um chifre verde neste campo, os ponteiros seria apreciada.
Solução
Eu faria o criptografar / sinal / decifrar / verificar métodos levam parâmetros para os dados em vez de ter variáveis ??de membro para eles. Tendo variáveis ??de membro para a chave e provedor parece que tudo bem. Basicamente eu esperaria de usar a mesma tecla várias vezes, mas não os mesmos dados.
Eu também torná-lo imutável -. Fazer todas as variáveis ??somente leitura, tendo todos os parâmetros que você precisa para os provedores no construtor em vez de ter um método de inicialização separado
Além disso, parece tudo bem para quebrar a funcionalidade de uma API simples para suas necessidades, porém, sim.
Outras dicas
Eu fiz alguns ajustes, aqui é o que os olhares de implementação como:
using System;
using System.Security.Cryptography;
namespace Agnus.Cipher
{
public class RSA : IDisposable
{
private RSACryptoServiceProvider rSAProviderThis;
private RSACryptoServiceProvider rSAProviderOther = null;
public string PublicKey
{
get { return rSAProviderThis.ToXmlString(false); }
}
public RSA()
{
rSAProviderThis = new RSACryptoServiceProvider { PersistKeyInCsp = true };
}
public void InitializeRSAProviderOther(string parameters)
{
rSAProviderOther.FromXmlString(parameters);
}
public byte[] Encrypt(byte[] plaintextBytes)
{
return rSAProviderThis.Encrypt(plaintextBytes, true);
}
public string Decrypt(byte[] ciphertextBytes)
{
try
{
return Convert.ToBase64String( rSAProviderThis.Decrypt(ciphertextBytes, true));
}
catch (CryptographicException ex)
{
Console.WriteLine("Unable to decrypt: " + ex.Message + " " + ex.StackTrace);
}
finally
{
this.Dispose();
}
return string.Empty;
}
public string SignData(byte[] ciphertextBytes)
{
string signature = GenerateSignature(ciphertextBytes, rSAProviderThis);
return signature;
}
private string GenerateSignature(byte[] ciphertextBytes, RSACryptoServiceProvider provider)
{
using (SHA1Managed SHA1 = new SHA1Managed())
{
byte[] hash = SHA1.ComputeHash(ciphertextBytes);
string signature = Convert.ToBase64String(provider.SignHash(hash, CryptoConfig.MapNameToOID("SHA1")));
return signature;
}
}
public string VerifySignature(byte[] ciphertextBytes, string parameters, string signatureToVerify)
{
InitializeRSAProviderOther(parameters);
string actualSignature = GenerateSignature(ciphertextBytes, rSAProviderOther);
if (actualSignature.Equals(signatureToVerify))
{
//verification successful
string decryptedData = this.Decrypt(ciphertextBytes);
return decryptedData;
//decryptedData is a symmetric key
}
else
{
//verification unsuccessful
//end session
}
return string.Empty;
}
#region IDisposable Members
public void Dispose()
{
if (rSAProviderOther != null)
{
rSAProviderOther.Clear();
}
rSAProviderThis.Clear();
GC.SuppressFinalize(this);
}
#endregion
}
}
vocês ainda não disse nada sobre como a comunicação vai ser estabelecida (eu estou pensando sockets). Por favor, me ilumine.
Eu não sei se isso snip de código pode ajudá-lo, eu escrevi este código para ser capaz de cripta e descriptografar com / pares de chaves públicas privadas em diversas algortims crypting e sem dados para emitir comprimento criptografar, RSA infact aplicação em .NET sofrer whe tentar gerenciar mais de 250 (mais ou menos, desculpe, eu não me lembro) bytes de dados.
Eu só cortar e colar apenas métodos necessários, eu também cortado causa de documentação xml não é em Inglês, se você encontrou este útil deixe-me saber, eu posso postar tudo fonte. Repito Eu não testei este cut & paste versão, mas eu usei a versão completa desta classe que não é muito diferente.
BTW: é em VB, mas se você só precisa se escondem em que eu acho que é o suficiente;)
Namespace Crypto
Public Class RSACry
Shared Sub New()
End Sub
Public Enum Algorithms
DES
TDES
RC2
RDAEL
End Enum
Public Shared Function Encrypt(ByVal xmlkeystring As String, ByVal typo As Algorithms, ByVal datatoencrypt As String) As String
Dim rsaer As RSA = Crypto.RSACry.ReadKeyString(xmlkeystring)
Dim result() As Byte = Crypto.RSACry.EncryptIt(rsaer, typo, datatoencrypt)
Return System.Convert.ToBase64String(result)
End Function
Public Shared Function Decrypt(ByVal xmlkeystring As String, ByVal typo As Algorithms, ByVal datatodecrypt As String) As String
Dim rsaer As RSA = Crypto.RSACry.ReadKeyString(xmlkeystring)
Dim result() As Byte = Crypto.RSACry.DecryptIt(rsaer, typo, datatodecrypt)
Return System.Text.Encoding.UTF8.GetString(result)
End Function
Friend Shared Function EncryptIt(ByRef rsaer As RSA, ByVal typo As Algorithms, ByVal datatoencrypt As String) As Byte()
Dim result() As Byte = Nothing
Try
Dim plainbytes() As Byte = System.Text.Encoding.UTF8.GetBytes(datatoencrypt)
Dim sa As SymmetricAlgorithm = SymmetricAlgorithm.Create(Crypto.RSACry.GetAlgorithmName(typo))
Dim ct As ICryptoTransform = sa.CreateEncryptor()
Dim encrypt() As Byte = ct.TransformFinalBlock(plainbytes, 0, plainbytes.Length)
Dim fmt As RSAPKCS1KeyExchangeFormatter = New RSAPKCS1KeyExchangeFormatter(rsaer)
Dim keyex() As Byte = fmt.CreateKeyExchange(sa.Key)
--return the key exchange, the IV (public) and encrypted data
result = New Byte(keyex.Length + sa.IV.Length + encrypt.Length) {}
Buffer.BlockCopy(keyex, 0, result, 0, keyex.Length)
Buffer.BlockCopy(sa.IV, 0, result, keyex.Length, sa.IV.Length)
Buffer.BlockCopy(encrypt, 0, result, keyex.Length + sa.IV.Length, encrypt.Length)
Catch ex As Exception
Throw New CryptographicException("Unable to crypt: " + ex.Message)
End Try
Return result
End Function
Friend Shared Function DecryptIt(ByRef rsaer As RSA, ByVal typo As Algorithms, ByVal datatodecrypt As String) As Byte()
Dim result() As Byte = Nothing
Try
Dim encrbytes() As Byte = System.Convert.FromBase64String(datatodecrypt)
Dim sa As SymmetricAlgorithm = SymmetricAlgorithm.Create(Crypto.RSACry.GetAlgorithmName(typo))
Dim keyex() As Byte = New Byte((rsaer.KeySize >> 3) - 1) {}
Buffer.BlockCopy(encrbytes, 0, keyex, 0, keyex.Length)
Dim def As RSAPKCS1KeyExchangeDeformatter = New RSAPKCS1KeyExchangeDeformatter(rsaer)
Dim key() As Byte = def.DecryptKeyExchange(keyex)
Dim iv() As Byte = New Byte((sa.IV.Length - 1)) {}
Buffer.BlockCopy(encrbytes, keyex.Length, iv, 0, iv.Length)
Dim ct As ICryptoTransform = sa.CreateDecryptor(key, iv)
result = ct.TransformFinalBlock(encrbytes, keyex.Length + iv.Length, (encrbytes.Length - 1) - (keyex.Length + iv.Length))
Catch ex As Exception
Throw New CryptographicException("Unable to decrypt: " + ex.Message)
End Try
Return result
End Function
Friend Shared Function GetAlgorithmName(ByVal typo As Algorithms) As String
Dim algtype As String = String.Empty
Select Case typo
Case Algorithms.DES
Return "DES"
Exit Select
Case Algorithms.RC2
Return "RC2"
Exit Select
Case Algorithms.RDAEL
Return "Rijndael"
Exit Select
Case Algorithms.TDES
Return "TripleDES"
Exit Select
Case Else
Return "Rijndael"
Exit Select
End Select
Return algtype
End Function
Friend Shared Function ReadKeyString(ByVal xmlkeystring As String) As RSA
Dim rsaer As RSA = Nothing
Try
If (String.IsNullOrEmpty(xmlkeystring)) Then Throw New Exception("Key is not specified")
rsaer = RSA.Create()
rsaer.FromXmlString(xmlkeystring)
Catch ex As Exception
Throw New CryptographicException("Unable to load key")
End Try
Return rsaer
End Function
End Namespace
@gogole: Na verdade eu não usar soquetes, este código é usado em stand caminho sozinho com teclas de mão-bringed. No entanto depois de ter seu mecanismo cripta, você está em mais da metade da jornada.
Aqui existem dois métodos que faltam para criar chaves, como pediu o código está completo. Espero que você encontrou este útil
Public Shared Sub CreateKeyPair(ByVal filename As String)
Dim xmlpublic As String = String.Empty
Dim xmlprivate As String = String.Empty
CreateKeyPair(xmlpublic, xmlprivate)
Try
Dim writer As New StreamWriter(filename + ".prv")
writer.Write(xmlprivate)
writer.Flush()
writer.Close()
Catch ex As Exception
Throw New CryptographicException("Unable to write private key file: " + ex.Message)
End Try
Try
Dim writer = New StreamWriter(filename + ".pub")
writer.Write(xmlpublic)
writer.Flush()
writer.Close()
Catch ex As Exception
Throw New CryptographicException("Unable to write public key file: " + ex.Message)
End Try
End Sub
Public Shared Sub CreateKeyPair(ByRef xmlpublic As String, ByRef xmlprivate As String)
Dim rsa As RSA = Nothing
Try
rsa.Create()
Catch ex As Exception
Throw New CryptographicException("Unable to initialize keys: " + ex.Message)
End Try
Try
xmlpublic = rsa.ToXmlString(True)
Catch ex As Exception
Throw New CryptographicException("Unable to generate public key: " + ex.Message)
End Try
Try
xmlprivate = rsa.ToXmlString(False)
Catch ex As Exception
Throw New CryptographicException("Unable to generate private key: " + ex.Message)
End Try
End Sub
Não sei muito vb mas tentou converter @Andrea Celin uma das Código útil em c #
namespace Crypto
{
using System;
using System.Security.Cryptography;
class RSACry
{
public enum Algorithms
{
DES,
TDES,
RC2,
RDAEL
};
public string Encrypt(string xmlkeystring, Algorithms typo, string datatoencrypt)
{
RSA rsaer = RSACry.ReadKeyString(xmlkeystring);
byte[] result = RSACry.EncryptIt(rsaer, typo, datatoencrypt);
return System.Convert.ToBase64String(result);
}
public string Decrypt(string xmlkeystring,Algorithms typo,string datatodecrypt)
{
RSA rsaer = RSACry.ReadKeyString(xmlkeystring);
byte[] result =RSACry.DecryptIt(rsaer, typo, datatodecrypt);
return System.Text.Encoding.UTF8.GetString(result);
}
public static byte[] EncryptIt(RSA rsaer, Algorithms typo, string datatoencrypt)
{
byte[] result = null;
try
{
byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(datatoencrypt);
SymmetricAlgorithm sa = SymmetricAlgorithm.Create(RSACry.GetAlgorithmName(typo));
ICryptoTransform ct = sa.CreateEncryptor();
byte[] encrypt = ct.TransformFinalBlock(plainbytes, 0, plainbytes.Length);
RSAPKCS1KeyExchangeFormatter fmt = new RSAPKCS1KeyExchangeFormatter(rsaer);
byte[] keyex = fmt.CreateKeyExchange(sa.Key);
//--return the key exchange, the IV (public) and encrypted data
result = new byte[keyex.Length + sa.IV.Length + encrypt.Length];
Buffer.BlockCopy(keyex, 0, result, 0, keyex.Length);
Buffer.BlockCopy(sa.IV, 0, result, keyex.Length, sa.IV.Length);
Buffer.BlockCopy(encrypt, 0, result, keyex.Length + sa.IV.Length, encrypt.Length);
}
catch (Exception ex)
{
throw new CryptographicException("Unable to crypt: " + ex.Message);
}
return result;
}
public static byte[] DecryptIt(RSA rsaer, Algorithms typo, string datatodecrypt)
{
byte[] result = null;
try
{
byte[] encrbytes = System.Convert.FromBase64String(datatodecrypt);
SymmetricAlgorithm sa = SymmetricAlgorithm.Create(RSACry.GetAlgorithmName(typo));
byte[] keyex = new byte[(rsaer.KeySize >> 3) - 1];
Buffer.BlockCopy(encrbytes, 0, keyex, 0, keyex.Length);
RSAPKCS1KeyExchangeDeformatter def = new RSAPKCS1KeyExchangeDeformatter(rsaer);
byte[] key = def.DecryptKeyExchange(keyex);
byte[] iv = new byte[sa.IV.Length - 1];
Buffer.BlockCopy(encrbytes, keyex.Length, iv, 0, iv.Length);
ICryptoTransform ct = sa.CreateDecryptor(key, iv);
result = ct.TransformFinalBlock(encrbytes, keyex.Length + iv.Length, (encrbytes.Length - 1) - (keyex.Length + iv.Length));
}
catch (Exception ex)
{
throw new CryptographicException("Unable to decrypt: " + ex.Message);
}
return result;
}
public static string GetAlgorithmName(Algorithms typo)
{
string algtype = String.Empty;
switch(typo)
{
case Algorithms.DES:
algtype = "DES";
break;
case Algorithms.RC2:
algtype = "RC2";
break;
case Algorithms.RDAEL:
algtype = "Rijndael";
break;
case Algorithms.TDES:
algtype = "TripleDES";
break;
default:
algtype = "Rijndael";
break;
}
return algtype;
}
public static RSA ReadKeyString(string xmlkeystring)
{
RSA rsaer = null;
try
{
if (String.IsNullOrEmpty(xmlkeystring))
{ throw new Exception("Key is not specified"); }
rsaer = RSA.Create();
rsaer.FromXmlString(xmlkeystring);
}
catch (Exception ex)
{
throw new CryptographicException("Unable to load key :"+ex.Message);
}
return rsaer;
}
}
}