如何将ASP.NET中基于SHA1的加解密逻辑迁移至Node.js
Hey there! I see you're looking to port your C# SHA1-based encryption logic to Node.js for your new Angular + Node project—let's break this down step by step to ensure it matches exactly with your existing ASP.NET code.
First, let's recap the key details from your C# code that we need to replicate:
- Uses PBKDF1 (via
PasswordDeriveByteswith SHA1) to generate the encryption key - AES-256-CBC encryption (since
RijndaelManagedwith 256-bit key defaults to AES) - Specific encodings: ASCII for IV/salt, UTF8 for plaintext, Base64 for ciphertext
- Fixed iteration count and key size
Node.js has a built-in crypto module that we can use, but we'll need to manually implement PBKDF1 since it's not natively supported (Node.js defaults to PBKDF2). Here's the complete implementation:
Step 1: Define Constants (Match C# Static Variables)
const crypto = require('crypto'); // Exact matches to your C# static variables const passPhrase = "Paaaa5p***"; const saltValue = "s@1t***lue"; const hashAlgorithm = "sha1"; const passwordIterations = 2; const initVector = "@1B2c3D4e5F6****"; const keySize = 256; // Key size in bits
Step 2: Replicate C#'s PasswordDeriveBytes Logic
C#'s PasswordDeriveBytes uses PBKDF1, which repeats hashing the passphrase + salt for the specified iterations. If the required key length exceeds SHA1's 20-byte output, it recursively generates additional bytes. Here's how to mimic that:
// Mimics C#'s PasswordDeriveBytes (PBKDF1 with SHA1) function passwordDeriveBytes(passphrase, salt, iterations, keyLength) { let derivedKey = Buffer.from(passphrase, 'utf8'); // Iterate the hash process as specified for (let i = 0; i < iterations; i++) { const hash = crypto.createHash(hashAlgorithm); hash.update(Buffer.concat([derivedKey, salt])); derivedKey = hash.digest(); } // Generate extra bytes if needed (when keyLength > SHA1's 20 bytes) if (derivedKey.length < keyLength) { const extraBytes = passwordDeriveBytes(passphrase, derivedKey, 1, keyLength - derivedKey.length); derivedKey = Buffer.concat([derivedKey, extraBytes]); } return derivedKey.slice(0, keyLength); }
Step 3: Encryption Function (Matches EncryptText)
This function mirrors your C# encryption logic, using the same encoding and AES parameters:
function encryptText(text) { // Convert IV and salt to ASCII bytes (matches C#'s Encoding.ASCII.GetBytes) const initVectorBytes = Buffer.from(initVector, 'ascii'); const saltValueBytes = Buffer.from(saltValue, 'ascii'); // Convert plaintext to UTF8 bytes (matches C#'s Encoding.UTF8.GetBytes) const plainTextBytes = Buffer.from(text, 'utf8'); // Generate the 256-bit key (32 bytes = 256/8) const keyBytes = passwordDeriveBytes(passPhrase, saltValueBytes, passwordIterations, keySize / 8); // Create AES-CBC cipher (C#'s RijndaelManaged defaults to AES-CBC with PKCS7 padding) const cipher = crypto.createCipheriv('aes-256-cbc', keyBytes, initVectorBytes); // Encrypt and combine results let cipherText = cipher.update(plainTextBytes); cipherText = Buffer.concat([cipherText, cipher.final()]); // Convert to Base64 string (matches C#'s Convert.ToBase64String) return cipherText.toString('base64'); }
Step 4: Decryption Function (Matches DecryptText)
This reverses the encryption process exactly like your C# code:
function decryptText(encryptText) { // Convert IV and salt to ASCII bytes const initVectorBytes = Buffer.from(initVector, 'ascii'); const saltValueBytes = Buffer.from(saltValue, 'ascii'); // Convert Base64 ciphertext to byte array (matches C#'s Convert.FromBase64String) const cipherTextBytes = Buffer.from(encryptText, 'base64'); // Generate the same 256-bit key const keyBytes = passwordDeriveBytes(passPhrase, saltValueBytes, passwordIterations, keySize / 8); // Create AES-CBC decipher const decipher = crypto.createDecipheriv('aes-256-cbc', keyBytes, initVectorBytes); // Decrypt and combine results let plainText = decipher.update(cipherTextBytes); plainText = Buffer.concat([plainText, decipher.final()]); // Convert UTF8 bytes back to string (matches C#'s Encoding.UTF8.GetString) return plainText.toString('utf8'); }
Step 5: Test the Implementation
You can verify this works with a quick test:
// Test with sample text const testPassword = "MySecurePassword123"; const encrypted = encryptText(testPassword); console.log("Encrypted:", encrypted); const decrypted = decryptText(encrypted); console.log("Decrypted:", decrypted); // Should output "MySecurePassword123"
Key Notes to Ensure Compatibility
- Encoding Consistency: We strictly use ASCII for IV/salt and UTF8 for plaintext, just like your C# code—changing these will break compatibility.
- PBKDF1 Accuracy: The custom
passwordDeriveBytesfunction exactly mimics C#'s behavior, including generating extra bytes when the key length exceeds SHA1's output. - AES Parameters: Node.js uses PKCS7 padding by default, which matches C#'s
RijndaelManageddefault padding. Don't change this unless your C# code uses a different padding scheme. - Iteration Count: Keep
passwordIterationsset to 2—any change will generate a different key, making decryption fail between Node.js and C#.
内容的提问来源于stack exchange,提问作者Majedur

