I have code to encrypt and decrypt the files in C#. Now I need to move the encryption task to front end an I need to write javascript code to encrypt the .xlsx files. Below is the C# code for encrypt and decrypt:
private void EncryptFile(string inputFile, string outputFile)
{
try
{
string password = #"myKey123"; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
string cryptFile = outputFile;
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateEncryptor(key, key),
CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
}
catch(Exception ex)
{
// MessageBox.Show("Encryption failed!", "Error");
}
}
private void DecryptFile(string inputFile, string outputFile)
{
{
string password = #"myKey123"; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateDecryptor(key, key),
CryptoStreamMode.Read);
FileStream fsOut = new FileStream(outputFile, FileMode.Create);
int data;
while ((data = cs.ReadByte()) != -1)
fsOut.WriteByte((byte)data);
fsOut.Close();
cs.Close();
fsCrypt.Close();
}
}
I tried to create a encryption in javascript as below:
var reader = new FileReader();
reader.onload = function (e) {
var encrypted = CryptoJS.AES.encrypt(e.target.result, 'myKey123');
var data = new FormData();
var encryptedFile = new File([encrypted], file.name, { type: "text/plain", lastModified: new Date() });
data.append('file', encryptedFile);
$.ajax({
url: 'http://localhost:57691/api/WithKey/UploadFile',
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function (data) {
debugger;
}
});
};
reader.readAsDataURL(file);
I also tried converting key to utf16 as below:
function wordsToBytes (words) {
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
return bytes;
}
var temp = CryptoJS.enc.Utf16.parse('myKey123');
var key = wordsToBytes(temp.words);
But no luck. Can you please someone help me where I am doing wrong. What is the right way to encrypt the file in javascript as same in C#?
This is the JavaScript code that would produce the same ciphertext as the C# code. The problem that remains is that you need to transmit this somehow.
var keyWords = CryptoJS.enc.Utf16LE.parse("myKey123");
var encryptedWords = CryptoJS.AES.encrypt("some string", keyWords, { iv: keyWords }).ciphertext;
console.log("Hex: " + encryptedWords.toString());
console.log("Base64: " + encryptedWords.toString(CryptoJS.enc.Base64));
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/enc-utf16-min.js"></script>
Related
This is the algorithm for signing the data in C# using a private key from a certificate that is used from both me and the client in order to define an unique key to identify the user:
X509Certificate2 keyStore = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "Certifikatat\\" + certPath, certPass, X509KeyStorageFlags.Exportable);
RSA privateKey = keyStore.GetRSAPrivateKey();
byte[] iicSignature = privateKey.SignData(Encoding.ASCII.GetBytes("K31418036C|2022-5-16 13:30:41|406|st271ir481|al492py609|zz463gy579|340"), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
byte[] iic = ((HashAlgorithm)CryptoConfig,CreateFromName("MD5")).ComputeHash(iicSignature);
I then pass the private key to my Javascript using Bouncy Castle:
X509Certificate2 keyStore = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "Certifikatat\\" + certPath, certPass, X509KeyStorageFlags.Exportable);
RSA privateKey = keyStore.GetRSAPrivateKey();
var eky = DotNetUtilities.GetRsaKeyPair(privateKey);
Pkcs8Generator pkcs8Gen = new Pkcs8Generator(eky.Private);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pkcs8 = pkcs8Gen.Generate();
PemWriter pemWriter = new PemWriter(new StringWriter());
pemWriter.WriteObject(pkcs8);
pemWriter.Writer.Flush();
return pemWriter.Writer.ToString();
This one is the algorithm used in Javascript:
window.crypto.subtle.importKey(
"pkcs8",
pemToArrayBuffer(pkcs8Pem), {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-256"
},
},
false, ["sign"]
)
.then(function(privateKey) {
console.log(privateKey);
// Sign: RSA with SHA256 and PKCS#1 v1.5 padding
window.crypto.subtle.sign({
name: "RSASSA-PKCS1-v1_5",
},
privateKey,
new TextEncoder().encode("K31418036C|2022-5-16 13:30:41|406|st271ir481|al492py609|zz463gy579|340")
)
.then(function(signature) {
var iic = md5(signature);
console.log(ab2b64(signature));
})
.catch(function(err) {
console.error(err);
});
})
.catch(function(err) {
console.error(err);
});
function ab2b64(arrayBuffer) {
return window.btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}
function removeLines(str) {
str = str.replace("\r", "");
return str.replace("\n", "");
}
function base64ToArrayBuffer(b64) {
var byteString = atob(b64);
var byteArray = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
byteArray[i] = byteString.charCodeAt(i);
}
return byteArray;
}
function pemToArrayBuffer(pem) {
var b64Lines = removeLines(pem);
var b64Prefix = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
var b64Final = b64Prefix.replace('-----END PRIVATE KEY-----', '');
return base64ToArrayBuffer(b64Final);
}
The signatures returned are different for some reason. I need them to be the same or else it's all pointless because the client won't be authenticated.
The results are as follow:
C#:
57CF663ACBEDE6305309682BA7261412
Javascript:
c099d176dcd95c59d748d6066dcd462e
I had to convert my signature to base64 and then encode it with atob() after that i needed this md5 library to hash the data and then use .toUpperCase() to reproduce the correct result.
The complete code looks like this:
md5(atob(ab2b64(signature))).toUpperCase();
Now i get the same result from both C# and JS.
I am currently implementing the RSA-OAEP encryption on Javascript and decryption at Java.
My javascript code has the following
function stringToArrayBuffer(str){
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function arrayBufferToString(str){
var byteArray = new Uint8Array(str);
var byteString = '';
for(var i=0; i < byteArray.byteLength; i++) {
byteString += String.fromCodePoint(byteArray[i]);
}
return byteString;
}
function encryptDataWithPublicKey(data, key) {
data = stringToArrayBuffer(data);
return window.crypto.subtle.encrypt(
{
name: "RSA-OAEP",
//label: Uint8Array([...]) //optional
},
key, //from generateKey or importKey above
data //ArrayBuffer of data you want to encrypt
);
}
var pem = Config.encryption.publicKey;
// fetch the part of the PEM string between header and footer
const pemHeader = "-----BEGIN PUBLIC KEY-----";
const pemFooter = "-----END PUBLIC KEY-----";
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
// base64 decode the string to get the binary data
const binaryDerString = window.atob(pemContents);
// convert from a binary string to an ArrayBuffer
const binaryDer = stringToArrayBuffer(binaryDerString);
window.crypto.subtle.importKey(
"spki",
binaryDer,
{
name: "RSA-OAEP",
hash: { name: "SHA-256" }
},
true,
["encrypt"]
).then(function (publicKey) {
encryptDataWithPublicKey(text, publicKey).then((result) => {
var rdata = arrayBufferToString(result);
resolve(rdata);
});
}).catch(function (err) {
console.log(err);
reject(err);
});
I also have a Java function to decrypt the text. Assume "rsaOaepCipherText" is a string text.
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(), oaepParams);
return new String(cipher.doFinal(Base64.decodeBase64(rsaOaepCipherText)), "UTF-8");
However I keep getting decryption error on Java, and currently stuck at this portion, is there any error I have done on my encryption on Javascript?
Ok found it. I forgot to include btoa on the string before send to backend.
it should be
encryptDataWithPublicKey(text, publicKey).then((result) => {
var rdata = arrayBufferToString(result);
var rResult = window.btoa(rdata);
resolve(rResult);
});
I am trying to upload large binary files from a web client to a .NET 4.6.1 Framework MVC API. These files could range anywhere from 5GB to 20GB.
I have tried splitting the file into chunks to upload each chunk and merge the results at the end, but the merged file is always corrupted. If I work with small files and don't split, the binary will work correctly. However, when I split and merge the file is "corrupted". It won't load or behave as expected.
I have looked all over and haven't seen a proper solution to this so i'm hoping someone can help me here.
I followed this https://forums.asp.net/t/1742612.aspx?How+to+upload+a+big+file+in+Mvc, but I can't get it to work and the corrected solution was never posted. I am keeping track of the order of files before merging on the server.
Javascript (Call to uploadData is made to initiate)
function uploadComplete(file) {
var formData = new FormData();
formData.append('fileName', file.name);
formData.append('completed', true);
var xhr3 = new XMLHttpRequest();
xhr3.open("POST", "api/CompleteUpload", true); //combine the chunks together
xhr3.send(formData);
return;
}
function uploadData(item) {
var blob = item.zipFile;
var BYTES_PER_CHUNK = 750000000; // sample chunk sizes.
var SIZE = blob.size;
//upload content
var start = 0;
var end = BYTES_PER_CHUNK;
var completed = 0;
var count = SIZE % BYTES_PER_CHUNK == 0 ? SIZE / BYTES_PER_CHUNK : Math.floor(SIZE / BYTES_PER_CHUNK) + 1;
while (start < SIZE) {
var chunk = blob.slice(start, end);
var xhr = new XMLHttpRequest();
xhr.onload = function () {
completed = completed + 1;
if (completed === count) {
uploadComplete(item.zipFile);
}
};
xhr.open("POST", "/api/MultiUpload", true);
xhr.setRequestHeader("contentType", false);
xhr.setRequestHeader("processData", false);
xhr.send(chunk);
start = end;
end = start + BYTES_PER_CHUNK;
}
}
Server Controller
//global vars
public static List<string> myList = new List<string>();
[HttpPost]
[Route("CompleteUpload")]
public string CompleteUpload()
{
var request = HttpContext.Current.Request;
//verify all parameters were defined
var form = request.Form;
string fileName;
bool completed;
if (!string.IsNullOrEmpty(request.Form["fileName"]) &&
!string.IsNullOrEmpty(request.Form["completed"]))
{
fileName = request.Form["fileName"];
completed = bool.Parse(request.Form["completed"]);
}
else
{
return "Invalid upload request";
}
if (completed)
{
string path = HttpContext.Current.Server.MapPath("~/Data/uploads/Tamp");
string newpath = Path.Combine(path, fileName);
string[] filePaths = Directory.GetFiles(path);
foreach (string item in myList)
{
MergeFiles(newpath, item);
}
}
//Remove all items from list after request is done
myList.Clear();
return "success";
}
private static void MergeFiles(string file1, string file2)
{
FileStream fs1 = null;
FileStream fs2 = null;
try
{
fs1 = System.IO.File.Open(file1, FileMode.Append);
fs2 = System.IO.File.Open(file2, FileMode.Open);
byte[] fs2Content = new byte[fs2.Length];
fs2.Read(fs2Content, 0, (int)fs2.Length);
fs1.Write(fs2Content, 0, (int)fs2.Length);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + " : " + ex.StackTrace + " " + file2);
}
finally
{
if(fs1 != null) fs1.Close();
if (fs2 != null)
{
fs2.Close();
System.IO.File.Delete(file2);
}
}
}
[HttpPost]
[Route("MultiUpload")]
public string MultiUpload()
{
try
{
var request = HttpContext.Current.Request;
var chunks = request.InputStream;
string path = HttpContext.Current.Server.MapPath("~/Data/uploads/Tamp");
string fileName = Path.GetTempFileName();
string newpath = Path.Combine(path, fileName);
myList.Add(newpath);
using (System.IO.FileStream fs = System.IO.File.Create(newpath))
{
byte[] bytes = new byte[77570];
int bytesRead;
while ((bytesRead = request.InputStream.Read(bytes, 0, bytes.Length)) > 0)
{
fs.Write(bytes, 0, bytesRead);
}
}
return "test";
}
catch (Exception exception)
{
return exception.Message;
}
}
I'm trying to do:
encrypt text using web.crypto,
decrypt text using AesCryptoServiceProvider
I didn't get any exceptions in my code, but the decryption doesn't match the plain text that i've encrypted
the plaintext bytes after decyption are the same, but the encoding to string doesn't work
I'm using random iv, the key and the plaintext are constant.
function cryptoSys(plaintext,KeyString){
var iVec=window.crypto.getRandomValues(new Uint8Array(16));
var encryptSuccessFunc=(encrypt)=> { AfterEncrypt(BytearrayToString(iVec),arraybufferTostring(encrypt));}
var ImportKeySuccessFunc= (keyObj)=>{
window.crypto.subtle.encrypt(
{name:"AES-CBC", iv:iVec},
keyObj,
StringToByteArray(plaintext)
).then(encryptSuccessFunc);
window.crypto.subtle.importKey(
"raw",
StringToByteArray(KeyString),
{name:"AES-CBC", length:128},
true,
["encrypt","decrypt"]
).then(ImportKeySuccessFunc);
}
After that I'm sending the iVec, ciphertext using json
function AfterEncrypt(iVec,ciphertext)
{
var plaintext="String to Encrypt";
if(iVec==null)
return;
var send2server= {"ciphertext":ciphertext,
"iVec":iVec,
"plaintext":plaintext};
var objectDataString = JSON.stringify(send2server);
sendJSONtoserver(objectDataString,"getDelayedBid");
}
I'm using the following utility functions :
function StringToByteArray(strString) {
var byteArray = new Uint8Array(strString.length);
for (var i=0; i<strString.length; i++) {
byteArray[i] = strString.charCodeAt(i);
}
return byteArray;
}
function BytearrayToString(arrayBuffer) {
var strString = "";
for (var i=0; i<arrayBuffer.byteLength; i++) {
strString += String.fromCharCode(arrayBuffer[i]);
}
return strString;
}
function arraybufferTostring(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
server side accepts the keys, and suppose to decrypt:
public string DecryptCipher(Encoding u16, string cipherText, string key,string iVec)
{
byte[] ciphertextBytes = clearZeros(u16.GetBytes(cipherText));
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 128; //minimun key length
aes.Key = clearZeros(u16.GetBytes(key));
aes.IV = clearZeros(u16.GetBytes(iVec));
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
ICryptoTransform cryptoTrans = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] plaintextBytes = cryptoTrans.TransformFinalBlock(ciphertextBytes, 0, ciphertextBytes.Length);
cryptoTrans.Dispose();
return Convert.ToBase64String(plaintextBytes);
}
I've got this utility function as well:
private byte [] clearZeros(byte [] bytearray)
{
byte[] ans = new byte[bytearray.Length / 2];
for (int i = 0; i < bytearray.Length / 2; i++)
ans[i] = bytearray[2 * i];
return ans;
}
I'm getting the paramters from anthor function:
public ActionResult getDelayedBid([FromBody] DelayedSubmission delaySub)
{
if (delaySub.ciphertext == null)
return Json("Failure", JsonRequestBehavior.AllowGet);
Encoding u16LE = Encoding.Unicode;
String decrypetedMessageLE = new Encryption().DecryptCipher(u16LE, delaySub.ciphertext, getKeyFromDB(), delaySub.iVec);
if (decrypetedMessageLE.Equals(delaySub.plaintext) )
{
return Json("Success", JsonRequestBehavior.AllowGet);
}
return Json("Failure", JsonRequestBehavior.AllowGet);
}
I've used wrong Ecoding,
instand of
Convert.ToBase64String(plaintextBytes);
I should've used
Encoding.ASCII.GetString(plaintextBytes);
I want to download multiple types of files using AJAX.
I decided to generate a get requests with file name as query string using AJAX and get the response as base64 string and then convert it to blob object and download/save it using hyperlink a tag along with download attribute.
Kindly help me, how to use the optimized way/code to convert file to base64 string and then revert the base64 string to blob?
ASP.Net httpHandler code is here:
Imports System
Imports System.Web
Imports System.IO
Public Class FileDownload : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim Request As HttpRequest = context.Request
Dim Response As HttpResponse = context.Response
Dim serverFile As String = Request.QueryString("filename")
Dim filePath As String = String.Empty
filePath = context.Server.MapPath("~") & "\" & serverFile
Dim file As New System.IO.FileInfo(filePath)
Try
If (file.Exists) Then
Response.Clear()
Response.ClearContent()
Using reader As New FileStream(filePath, FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
Dim buffer As Byte() = New Byte(reader.Length - 1) {}
reader.Read(buffer, 0, CInt(reader.Length))
Response.Write(Convert.ToBase64String(buffer))
End Using
Response.Flush()
Response.End()
Else
Response.Write("File Not Found!")
Response.StatusCode = 500
End If
Catch ex As Exception
Response.Write(ex.ToString)
Response.StatusCode = 500
context.ApplicationInstance.CompleteRequest()
End Try
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Javascript function called with httphadler's application path as url and client side file name as filename
function downloadFileByAjax(filename, url) {
$.ajax({
type: 'GET',
url: url,
responseType: 'arraybuffer',
downloadid: filename,
success: function (result, status, xhr) {
var octetStreamMime = 'application/octet-stream';
var filename = this.downloadid;
var contentType = xhr.getResponseHeader('content-type') || octetStreamMime;
var a = document.createElement('a');
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if (urlCreator && window.Blob && ('download' in a) && window.atob) {
var blob = base64ToBlob(result, contentType);
var url = window.URL.createObjectURL(blob);
a.setAttribute('href', url);
a.setAttribute("download", filename);
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(event);
//window.URL.revokeObjectURL(url);
}
},
error: function (xhr, msg, error) {
//console.log(xhr, msg, error);
//console.log(xhr.responseText);
console.log(msg);
},
complete: function (xhr, status) {
//console.log('completed');
}
});
}
function base64ToBlob(base64, mimetype, slicesize) {
if (!window.atob || !window.Uint8Array) {
console.log('The current browser doesnot have the atob function. Cannot continue');
return null;
}
mimetype = mimetype || '';
slicesize = slicesize || 512;
var bytechars = atob(base64);
var bytearrays = [];
for (var offset = 0; offset < bytechars.length; offset += slicesize) {
var slice = bytechars.slice(offset, offset + slicesize);
var bytenums = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
bytenums[i] = slice.charCodeAt(i);
}
var bytearray = new Uint8Array(bytenums);
bytearrays[bytearrays.length] = bytearray;
}
return new Blob(bytearrays, { type: mimetype });
}
To convert a file to base64 string use the following code
string data;
using (FileStream fs = new FileStream(dir + fileName, FileMode.Open, FileAccess.Read)) {
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, (int)fs.Length);
data = Convert.ToBase64String(buffer);
fs.Close();
}
return data;
In the success function of the ajax call convert the string to blob using the following code
var byteCharacters = atob(data);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = null;
blob = new Blob([byteArray], { type: 'text/plain' });
blob = new Blob([byteArray], { type: 'application/pdf;base64' });
var blobUrl = URL.createObjectURL(blob);
Depending upon the file format you can specify the type attribute for the blob and then assign the blobUrl as the source to your anchor tag