একটি গো প্রোগ্রাম সি # তে পোর্ট করার চেষ্টা করে আমারও একই সমস্যা হয়েছিল। এর অর্থ হ'ল গো প্রোগ্রামের সাথে ইতিমধ্যে প্রচুর ডেটা এনক্রিপ্ট করা হয়েছে। এই ডেটা এখন সি # দিয়ে ডিক্রিপ্ট করা উচিত।
চূড়ান্ত সমাধান ছিল PaddingMode.None
বা বরং PaddingMode.Zeros
।
গো-তে ক্রিপ্টোগ্রাফিক পদ্ধতি:
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha1"
"encoding/base64"
"io/ioutil"
"log"
"golang.org/x/crypto/pbkdf2"
)
func decryptFile(filename string, saltBytes []byte, masterPassword []byte) (artifact string) {
const (
keyLength int = 256
rfc2898Iterations int = 6
)
var (
encryptedBytesBase64 []byte // The encrypted bytes as base64 chars
encryptedBytes []byte // The encrypted bytes
)
// Load an encrypted file:
if bytes, bytesErr := ioutil.ReadFile(filename); bytesErr != nil {
log.Printf("[%s] There was an error while reading the encrypted file: %s\n", filename, bytesErr.Error())
return
} else {
encryptedBytesBase64 = bytes
}
// Decode base64:
decodedBytes := make([]byte, len(encryptedBytesBase64))
if countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64); decodedErr != nil {
log.Printf("[%s] An error occur while decoding base64 data: %s\n", filename, decodedErr.Error())
return
} else {
encryptedBytes = decodedBytes[:countDecoded]
}
// Derive key and vector out of the master password and the salt cf. RFC 2898:
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
// Create an AES cipher:
if aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
return
} else {
// CBC mode always works in whole blocks.
if len(encryptedBytes)%aes.BlockSize != 0 {
log.Printf("[%s] The encrypted data's length is not a multiple of the block size.\n", filename)
return
}
// Reserve memory for decrypted data. By definition (cf. AES-CBC), it must be the same lenght as the encrypted data:
decryptedData := make([]byte, len(encryptedBytes))
// Create the decrypter:
aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
// Decrypt the data:
aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)
// Cast the decrypted data to string:
artifact = string(decryptedData)
}
return
}
... এবং ...
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha1"
"encoding/base64"
"github.com/twinj/uuid"
"golang.org/x/crypto/pbkdf2"
"io/ioutil"
"log"
"math"
"os"
)
func encryptFile(filename, artifact string, masterPassword []byte) (status bool) {
const (
keyLength int = 256
rfc2898Iterations int = 6
)
status = false
secretBytesDecrypted := []byte(artifact)
// Create new salt:
saltBytes := uuid.NewV4().Bytes()
// Derive key and vector out of the master password and the salt cf. RFC 2898:
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
// Create an AES cipher:
if aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
return
} else {
// CBC mode always works in whole blocks.
if len(secretBytesDecrypted)%aes.BlockSize != 0 {
numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
secretBytesDecrypted = enhanced
}
// Reserve memory for encrypted data. By definition (cf. AES-CBC), it must be the same lenght as the plaintext data:
encryptedData := make([]byte, len(secretBytesDecrypted))
// Create the encrypter:
aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
// Encrypt the data:
aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)
// Encode base64:
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
base64.StdEncoding.Encode(encodedBytes, encryptedData)
// Allocate memory for the final file's content:
fileContent := make([]byte, len(saltBytes))
copy(fileContent, saltBytes)
fileContent = append(fileContent, 10)
fileContent = append(fileContent, encodedBytes...)
// Write the data into a new file. This ensures, that at least the old version is healthy in case that the
// computer hangs while writing out the file. After a successfully write operation, the old file could be
// deleted and the new one could be renamed.
if writeErr := ioutil.WriteFile(filename+"-update.txt", fileContent, 0644); writeErr != nil {
log.Printf("[%s] Was not able to write out the updated file: %s\n", filename, writeErr.Error())
return
} else {
if renameErr := os.Rename(filename+"-update.txt", filename); renameErr != nil {
log.Printf("[%s] Was not able to rename the updated file: %s\n", fileContent, renameErr.Error())
} else {
status = true
return
}
}
return
}
}
এখন, সি # তে ডিক্রিপশন:
public static string FromFile(string filename, byte[] saltBytes, string masterPassword)
{
var iterations = 6;
var keyLength = 256;
var blockSize = 128;
var result = string.Empty;
var encryptedBytesBase64 = File.ReadAllBytes(filename);
// bytes -> string:
var encryptedBytesBase64String = System.Text.Encoding.UTF8.GetString(encryptedBytesBase64);
// Decode base64:
var encryptedBytes = Convert.FromBase64String(encryptedBytesBase64String);
var keyVectorObj = new Rfc2898DeriveBytes(masterPassword, saltBytes.Length, iterations);
keyVectorObj.Salt = saltBytes;
Span<byte> keyVectorData = keyVectorObj.GetBytes(keyLength / 8 + blockSize / 8);
var key = keyVectorData.Slice(0, keyLength / 8);
var iv = keyVectorData.Slice(keyLength / 8);
var aes = Aes.Create();
aes.Padding = PaddingMode.Zeros;
// or ... aes.Padding = PaddingMode.None;
var decryptor = aes.CreateDecryptor(key.ToArray(), iv.ToArray());
var decryptedString = string.Empty;
using (var memoryStream = new MemoryStream(encryptedBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (var reader = new StreamReader(cryptoStream))
{
decryptedString = reader.ReadToEnd();
}
}
}
return result;
}
প্যাডিং সহ সমস্যাটি কীভাবে ব্যাখ্যা করা যেতে পারে? এনক্রিপশনের ঠিক আগে গো প্রোগ্রামটি প্যাডিং চেক করে:
// CBC mode always works in whole blocks.
if len(secretBytesDecrypted)%aes.BlockSize != 0 {
numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
secretBytesDecrypted = enhanced
}
গুরুত্বপূর্ণ অংশটি হ'ল:
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
উপযুক্ত দৈর্ঘ্যের সাহায্যে একটি নতুন অ্যারে তৈরি করা হয়, যাতে দৈর্ঘ্যটি ব্লকের আকারের একাধিক। এই নতুন অ্যারেটি শূন্যে ভরা। অনুলিপি পদ্ধতিটি এর মধ্যে বিদ্যমান ডেটা অনুলিপি করে। এটি নিশ্চিত করা হয়েছে যে নতুন অ্যারে বিদ্যমান ডেটার চেয়ে বড়। তদনুসারে, অ্যারের শেষে জিরো রয়েছে।
সুতরাং, সি # কোড ব্যবহার করতে পারেন PaddingMode.Zeros
। বিকল্পটি PaddingMode.None
কেবল কোনও প্যাডিং উপেক্ষা করে, যা কাজ করে। আমি আশা করি যে এই উত্তরটি যে কারও কাছে যান # # সি থেকে শুরু করে কোড ইত্যাদি পোর্ট করতে হয় তাদের জন্য সহায়ক is