AES и SHA256 Шифрование/дешифрование между C# и RubyC#

Место общения программистов C#
Ответить
Anonymous
 AES и SHA256 Шифрование/дешифрование между C# и Ruby

Сообщение Anonymous »

Процесс шифрования и расшифровки моего кода не соответствует процессу банка. То есть, когда я использую свой код для шифрования и расшифровки, все работает нормально. Но когда я пытаюсь отправить результат шифрования моего кода в банк, они отвечают мне < /p>
{
"isOk": false,
"message": "Error: Padding is invalid and cannot be removed.",
"body": null
}
< /code>
Код C#, который я пытаюсь адаптироваться к Ruby < /p>
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Text;

// Example JSON
string json = "{\"Empresa\":\"BCP\",\"año\":2025,\"ciudad\":\"La Paz\"}";

// Encryption and signing process
var textEncrypted = Encrypt(json, LoadCertificateBusiness());
var signature = SignWithCertificate(textEncrypted);

// Encryption and signing response
Console.WriteLine("Encrypted Data:");
Console.WriteLine(textEncrypted);
Console.WriteLine("Signature:");
Console.WriteLine(signature);

// Verification and decryption
if (VerifySignature(textEncrypted, signature))
{
Console.WriteLine("Decrypted Data:");
Console.WriteLine(Decrypt(textEncrypted, LoadCertificateBusiness()));
}

///
/// Encrypts plain text using the BUSINESS certificate
///
/// Text to encrypt
/// BUSINESS certificate for encryption
/// Encrypted text in Base64
static string Encrypt(string plainText, X509Certificate2 certificate)
{
var bytesToEncrypted = Encoding.UTF8.GetBytes(plainText);
var publicKeyBytes = certificate.GetPublicKey();
var passwordBytes = SHA256.Create().ComputeHash(publicKeyBytes);
var bytesEncrypted = EncryptAES(bytesToEncrypted, passwordBytes);
return Convert.ToBase64String(bytesEncrypted);
}

///
/// Decrypts text using the BUSINESS certificate
///
/// Encrypted text in Base64
/// BUSINESS certificate for decryption
/// Decrypted text
static string Decrypt(string encryptedText, X509Certificate2 certificate)
{
if (encryptedText == null!)
{
return null!;
}

var bytesToBeDecrypted = Convert.FromBase64String(encryptedText);
var key = certificate.GetPublicKey();
var passwordBytes = SHA256.Create().ComputeHash(key);
var bytesDecrypted = DecryptAES(bytesToBeDecrypted, passwordBytes);
return Encoding.UTF8.GetString(bytesDecrypted);
}

///
/// AES-256-CBC encryption with PBKDF2
///
/// Bytes to encrypt
/// Key derived from certificate
/// Encrypted bytes
static byte[] EncryptAES(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null!;
var saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

using (MemoryStream ms = new MemoryStream())
{
using (Aes AES = Aes.Create())
{
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;

using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}

return encryptedBytes;
}

///
/// AES-256-CBC decryption with PBKDF2
///
/// Bytes to decrypt
/// Key derived from certificate
/// Decrypted bytes
static byte[] DecryptAES(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null!;
var saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

using (MemoryStream ms = new MemoryStream())
{
using (Aes AES = Aes.Create())
{
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;

using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}

return decryptedBytes;
}

///
/// Signs data using the ENC_DESA certificate
///
/// Data to sign
/// Digital signature in Base64
string SignWithCertificate(string data)
{
X509Certificate2 certificate = LoadCertificateEnc_Desa();

using (RSA rsa = certificate.GetRSAPrivateKey()!)
{
byte[] dataBytes = Encoding.UTF8.GetBytes(data);

using (SHA256 sha256 = SHA256.Create())
{
byte[] byteHash = sha256.ComputeHash(dataBytes);
byte[] signatureHash = rsa.SignHash(byteHash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signatureHash);
}
}
}

///
/// Loads the BUSINESS certificate from the local store
///
/// BUSINESS certificate for encryption
X509Certificate2 LoadCertificateBusiness()
{
string certificateThumbPrint = ""; // BUSINESS certificate thumbprint
var x509CertificatesStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
x509CertificatesStore.Open(OpenFlags.ReadOnly);

var certCollection = x509CertificatesStore.Certificates.Find(
X509FindType.FindByThumbprint,
certificateThumbPrint,
false
);

X509Certificate2 certPublic = certCollection.OfType().FirstOrDefault()!;
x509CertificatesStore.Close();

return certPublic;
}

///
/// Loads the ENC_DESA certificate from the local store
///
/// ENC_DESA certificate for signing
X509Certificate2 LoadCertificateEnc_Desa()
{
string certificateThumbPrint = ""; // ENC_DESA certificate thumbprint
var x509CertificatesStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
x509CertificatesStore.Open(OpenFlags.ReadOnly);

var certCollection = x509CertificatesStore.Certificates.Find(
X509FindType.FindByThumbprint,
certificateThumbPrint,
false
);

X509Certificate2 certPrivate = certCollection.OfType().FirstOrDefault()!;
x509CertificatesStore.Close();

return certPrivate;
}

///
/// Verifies the digital signature using the ENC_DESA certificate
///
/// Original data
/// Signature to verify
/// True if the signature is valid
bool VerifySignature(string data, string signature)
{
X509Certificate2 certificate = LoadCertificateEnc_Desa();

using (RSA rsa = certificate.GetRSAPublicKey()!)
{
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
byte[] signatureBytes = Convert.FromBase64String(signature);

return rsa.VerifyData(
dataBytes,
signatureBytes,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1
);
}
}

/*
IMPORTANT NOTES:

• The JSON must be encrypted with the BUSINESS certificate key
• The encryption result must be signed with the ENC_DESA certificate key
*/
< /code>
мой код до сих пор < /p>
require 'openssl'
require 'base64'
require 'json'
require 'digest'

class SecurityManagerV2
SALT_BYTES = [1, 2, 3, 4, 5, 6, 7, 8].pack('C*').freeze
PBKDF2_ITERATIONS = 1000
AES_KEY_SIZE = 32 # 256 bits / 8
AES_IV_SIZE = 16 # 128 bits / 8

# Certificate paths and passwords
ENC_DESA_PFX_PATH = Rails.root.join('config', 'certificates', 'EXAMPLE.pfx').to_s.freeze
BUSINESS_CER_PATH = Rails.root.join('config', 'certificates', 'EXAMPLE.cer').to_s.freeze

ENC_DESA_PASSWORD = 'password'.freeze
BUSINESS_PASSWORD = 'password'.freeze

# Encrypt plaintext using certificate's public key with AES
def self.encrypt(plain_text, certificate)
bytes_to_encrypted = plain_text.encode('UTF-8')
public_key_bytes = certificate.public_key.to_der
password_bytes = Digest::SHA256.digest(public_key_bytes)
bytes_encrypted = encrypt_aes(bytes_to_encrypted, password_bytes)
Base64.strict_encode64(bytes_encrypted)
end

# Decrypt encrypted text using certificate's public key
def self.decrypt(encrypted_text, certificate)
return nil if encrypted_text.nil?

bytes_to_be_decrypted = Base64.strict_decode64(encrypted_text)
key = certificate.public_key.to_der
password_bytes = Digest::SHA256.digest(key)
bytes_decrypted = decrypt_aes(bytes_to_be_decrypted, password_bytes)
bytes_decrypted.force_encoding('UTF-8')
end

# AES encryption using PBKDF2 key derivation
def self.encrypt_aes(bytes_to_be_encrypted, password_bytes)
# Derive key and IV using PBKDF2 (equivalent to Rfc2898DeriveBytes)
key_iv = OpenSSL::PKCS5.pbkdf2_hmac(
password_bytes,
SALT_BYTES,
PBKDF2_ITERATIONS,
AES_KEY_SIZE + AES_IV_SIZE,
OpenSSL::Digest.new('SHA1')
)

key = key_iv[0, AES_KEY_SIZE]
iv = key_iv[AES_KEY_SIZE, AES_IV_SIZE]

# Create AES cipher
cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.encrypt
cipher.key = key
cipher.iv = iv

# Encrypt data
cipher.update(bytes_to_be_encrypted) + cipher.final
end

# AES decryption using PBKDF2 key derivation
def self.decrypt_aes(bytes_to_be_decrypted, password_bytes)
# Derive key and IV using PBKDF2 (equivalent to Rfc2898DeriveBytes)
key_iv = OpenSSL::PKCS5.pbkdf2_hmac(
password_bytes,
SALT_BYTES,
PBKDF2_ITERATIONS,
AES_KEY_SIZE + AES_IV_SIZE,
OpenSSL::Digest.new('SHA1')
)

key = key_iv[0, AES_KEY_SIZE]
iv = key_iv[AES_KEY_SIZE, AES_IV_SIZE]

# Create AES decipher
decipher = OpenSSL::Cipher.new('AES-256-CBC')
decipher.decrypt
decipher.key = key
decipher.iv = iv

# Decrypt data
decipher.update(bytes_to_be_decrypted) + decipher.final
end

# Sign data with certificate's private key
def self.sign_with_certificate(data, certificate)
puts '[DEBUG] Starting digital signing process'

private_key = certificate.private_key
unless private_key
error_msg = 'Private key not available in certificate. Cannot sign data.'
puts "[ERROR] #{error_msg}"
raise error_msg
end

puts '[DEBUG] Private key available for signing'
data_bytes = data.encode('UTF-8')
puts "[DEBUG] Data to sign length: #{data_bytes.length} bytes"

# Create SHA256 hash of the data first
hash = Digest::SHA256.digest(data_bytes)
puts "[DEBUG] SHA256 hash created, length: #{hash.length} bytes"
puts "[DEBUG] Hash hex: #{Digest::SHA256.hexdigest(data_bytes)}"

# Sign using the data directly (not the hash) - this is equivalent to C# SignData
signature_hash = private_key.sign(OpenSSL::Digest.new('SHA256'), data_bytes)
puts "[DEBUG] Digital signature created, length: #{signature_hash.length} bytes"

encoded_signature = Base64.strict_encode64(signature_hash)
puts "[DEBUG] Signature encoded to Base64, length: #{encoded_signature.length} characters"

encoded_signature
rescue StandardError => e
error_msg = "Failed to sign data: #{e.message}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

# Load business certificate (for encryption/decryption)
def self.load_certificate_business
puts '[DEBUG] Loading business certificate for encryption/decryption'
load_certificate_from_cer(BUSINESS_CER_PATH)
end

# Load encryption/development certificate (for signing/verification)
def self.load_certificate_enc_desa
puts '[DEBUG] Loading ENC_DESA certificate for signing/verification'
load_certificate_from_pfx(ENC_DESA_PFX_PATH, ENC_DESA_PASSWORD)
end

# Verify signature with certificate's public key
def self.verify_signature(data, signature, certificate)
puts '[DEBUG] Starting signature verification'

public_key = certificate.public_key
puts '[DEBUG] Public key available for verification'

data_bytes = data.encode('UTF-8')
puts "[DEBUG] Data to verify length: #{data_bytes.length} bytes"

signature_bytes = Base64.strict_decode64(signature)
puts "[DEBUG] Signature decoded from Base64, length: #{signature_bytes.length} bytes"

# Use verify method which handles the hashing internally (equivalent to VerifyData in C#)
verification_result = public_key.verify(OpenSSL::Digest.new('SHA256'), signature_bytes, data_bytes)
puts "[DEBUG] Signature verification result: #{verification_result}"

verification_result
rescue StandardError => e
error_msg = "Failed to verify signature: #{e.message}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

# Debug method to help troubleshoot signature verification issues
def self.debug_signature_verification(data, signature, certificate)
puts '[DEBUG] ===== SIGNATURE VERIFICATION DEBUG ====='

begin
# Check if we're using the right certificate
puts "[DEBUG] Certificate for verification: #{certificate.certificate.subject}"
puts "[DEBUG] Certificate has private key: #{!certificate.private_key.nil?}"

# Check data integrity
data_bytes = data.encode('UTF-8')
data_hash = Digest::SHA256.hexdigest(data_bytes)
puts "[DEBUG] Data SHA256 hash: #{data_hash}"

# Check signature integrity
signature_bytes = Base64.strict_decode64(signature)
signature_hash = Digest::SHA256.hexdigest(signature_bytes)
puts "[DEBUG] Signature SHA256 hash: #{signature_hash}"

# Try manual verification process
puts '[DEBUG] Attempting manual verification...'
public_key = certificate.public_key

# Create hash of data (same as signing process)
hash = Digest::SHA256.digest(data_bytes)
puts "[DEBUG] Data hash for verification: #{Digest::SHA256.hexdigest(hash)}"

# Try verification with raw hash
manual_result = public_key.verify(OpenSSL::Digest.new('SHA256'), signature_bytes, data_bytes)
puts "[DEBUG] Manual verification result: #{manual_result}"

# If we have the private key, let's try re-signing to compare
if certificate.private_key
puts '[DEBUG] Re-signing data with same certificate for comparison...'
test_signature = sign_with_certificate(data, certificate)
puts "[DEBUG] Original signature: #{signature[0..50]}..."
puts "[DEBUG] Test signature: #{test_signature[0..50]}..."
puts "[DEBUG] Signatures match: #{signature == test_signature}"
end
rescue StandardError => e
puts "[ERROR] Debug verification failed: #{e.message}"
end

puts '[DEBUG] ===== END DEBUG ====='
end

# Load certificate from PFX file (PKCS#12 format with private key)
def self.load_certificate_from_pfx(pfx_path, password)
puts "[DEBUG] Loading PFX certificate from: #{pfx_path}"

# File existence validation
unless File.exist?(pfx_path)
error_msg = "PFX file not found: #{pfx_path}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

# File readability validation
unless File.readable?(pfx_path)
error_msg = "PFX file is not readable: #{pfx_path}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

file_size = File.size(pfx_path)
puts "[DEBUG] PFX file size: #{file_size} bytes"

if file_size.zero?
error_msg = "PFX file is empty: #{pfx_path}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

pfx_data = File.read(pfx_path)
puts '[DEBUG] Successfully read PFX file data'

# Load PKCS12 with password
pkcs12 = OpenSSL::PKCS12.new(pfx_data, password)
puts '[DEBUG] Successfully parsed PKCS12 structure'

# Validate certificate and key
unless pkcs12.certificate
error_msg = 'No certificate found in PFX file'
puts "[ERROR] #{error_msg}"
raise error_msg
end

unless pkcs12.key
error_msg = 'No private key found in PFX file'
puts "[ERROR] #{error_msg}"
raise error_msg
end

puts "[DEBUG] Certificate subject: #{pkcs12.certificate.subject}"
puts "[DEBUG] Certificate issuer: #{pkcs12.certificate.issuer}"
puts "[DEBUG] Certificate valid from: #{pkcs12.certificate.not_before}"
puts "[DEBUG] Certificate valid until: #{pkcs12.certificate.not_after}"
puts "[DEBUG] Private key type: #{pkcs12.key.class}"

# Validate certificate is not expired
if pkcs12.certificate.not_after < Time.now
puts "[WARNING] Certificate has expired on #{pkcs12.certificate.not_after}"
end

if pkcs12.certificate.not_before > Time.now
puts "[WARNING] Certificate is not yet valid until #{pkcs12.certificate.not_before}"
end

puts '[SUCCESS] PFX certificate loaded successfully'
CertificateWithKey.new(pkcs12.certificate, pkcs12.key)
rescue OpenSSL::PKCS12::PKCS12Error => e
error_msg = "Failed to load PFX certificate: #{e.message}. Possible causes: wrong password, corrupted file, or invalid PKCS12 format"
puts "[ERROR] #{error_msg}"
raise error_msg
rescue StandardError => e
error_msg = "Unexpected error loading PFX certificate: #{e.message}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

# Load certificate from CER file (X.509 format, public key only)
def self.load_certificate_from_cer(cer_path)
puts "[DEBUG] Loading CER certificate from: #{cer_path}"

# File existence validation
unless File.exist?(cer_path)
error_msg = "CER file not found: #{cer_path}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

# File readability validation
unless File.readable?(cer_path)
error_msg = "CER file is not readable: #{cer_path}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

file_size = File.size(cer_path)
puts "[DEBUG] CER file size: #{file_size} bytes"

if file_size.zero?
error_msg = "CER file is empty: #{cer_path}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

cer_data = File.read(cer_path)
puts '[DEBUG] Successfully read CER file data'

# Load X.509 certificate (DER format expected)
certificate = OpenSSL::X509::Certificate.new(cer_data)
puts '[DEBUG] Successfully parsed X.509 certificate'

puts "[DEBUG] Certificate subject: #{certificate.subject}"
puts "[DEBUG] Certificate issuer: #{certificate.issuer}"
puts "[DEBUG] Certificate valid from: #{certificate.not_before}"
puts "[DEBUG] Certificate valid until: #{certificate.not_after}"
puts "[DEBUG] Public key type: #{certificate.public_key.class}"

# Validate certificate is not expired
puts "[WARNING] Certificate has expired on #{certificate.not_after}" if certificate.not_after < Time.now

puts "[WARNING] Certificate is not yet valid until #{certificate.not_before}" if certificate.not_before > Time.now

puts '[SUCCESS] CER certificate loaded successfully'
CertificateWithKey.new(certificate, nil)
rescue OpenSSL::X509::CertificateError => e
error_msg = "Failed to load CER certificate: #{e.message}. File may not be a valid X.509 certificate in DER format"
puts "[ERROR] #{error_msg}"
raise error_msg
rescue StandardError => e
error_msg = "Unexpected error loading CER certificate: #{e.message}"
puts "[ERROR] #{error_msg}"
raise error_msg
end

# Helper class to hold certificate and private key together
class CertificateWithKey
attr_reader :certificate, :private_key

def initialize(certificate, private_key)
@certificate = certificate
@private_key = private_key
puts "[DEBUG] CertificateWithKey initialized with #{private_key ? 'private key' : 'public key only'}"
end

def public_key
@certificate.public_key
end

def to_der
@certificate.to_der
end

def to_pem
@certificate.to_pem
end
end
end
< /code>
Что мне не хватает? Мое шифрование предоставляет ряд размера 3180, а банк содержит 2880.{
"companyId": 1111,
"password": "example",
"documentNumber": "4850147",
"documentType": "Q",
"documentExtension": "LP",
"documentComplement": " ",
"amount": 5196.33,
"currency": "BOL",
"fundSource": "PRUEBA",
"fundDestination": "PRUEBA",
"sourceAccount": "2011040905323",
"sourceCurrency": "BOL",
"description": "PRUEBA",
"sendVouchers": "prueba@gmail.com",
"cismartApprovers": [
{
"idc": "00255921-Q-LP 1A",
"type": 1
}
],
"spreadsheet": {
"formAchPayments": [
{
"paymentType": "ACH",
"line": 1,
"accountNumber": "4027922806",
"titularName": "TUMOMO SRL",
"firstLastName": "TUMOMO SRL",
"secondLastName": " ",
"amount": 388,
"branchOfficeId": 201,
"firstDetail": "Detalle 1",
"mail": "",
"bankId": "1003",
"documentNumber": "123456789",
"documentType": "Q",
"documentExtension": "LP",
"documentComplement": ""
},
{
"paymentType": "ACH",
"line": 2,
"accountNumber": "4027922806",
"titularName": "TU SHOP ALANIS DANIELA MENDOZA",
"firstLastName": "TU SHOP ALANIS DANIELA MENDOZA",
"secondLastName": " ",
"amount": 2699.93,
"branchOfficeId": 201,
"firstDetail": "Detalle 1",
"mail": "",
"bankId": "1003",
"documentNumber": "123456789",
"documentType": "Q",
"documentExtension": "LP",
"documentComplement": ""
},
{
"paymentType": "ACH",
"line": 3,
"accountNumber": "4027922806",
"titularName": "LOS TIEMPOS - EDITORIAL CANELA",
"firstLastName": "LOS TIEMPOS - EDITORIAL CANELA",
"secondLastName": " ",
"amount": 2108.4,
"branchOfficeId": 201,
"firstDetail": "Detalle 1",
"mail": "",
"bankId": "1003",
"documentNumber": "123456789",
"documentType": "Q",
"documentExtension": "LP",
"documentComplement": ""
}
]
}
}


Подробнее здесь: https://stackoverflow.com/questions/797 ... p-and-ruby
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»