Процесс шифрования и дешифрования в моем коде не соответствует процессу банка. Другими словами, когда я использую свой код для шифрования и расшифровки, все работает нормально. Но когда я пытаюсь отправить результат шифрования моего кода в банк, они отвечают, что я не могу расшифровать его, потому что процессы шифрования не совпадают. Теперь моя система расшифровывает информацию после получения вашего ответа.{
"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
AES и SHA256 Шифрование/дешифрование между C# и Ruby ⇐ C#
Место общения программистов C#
-
Anonymous
1758091880
Anonymous
Процесс шифрования и дешифрования в моем коде не соответствует процессу банка. Другими словами, когда я использую свой код для шифрования и расшифровки, все работает нормально. Но когда я пытаюсь отправить результат шифрования моего кода в банк, они отвечают, что я не могу расшифровать его, потому что процессы шифрования не совпадают. Теперь моя система расшифровывает информацию после получения вашего ответа.{
"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": ""
}
]
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79766864/aes-and-sha256-encryption-decription-between-c-sharp-and-ruby[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия