core.js:478 Uncaught Error: Malformed UTF-8 Data When Decrypting Url Query Param in Angular - node.js

I have a crypto encrypt and decrypt service below and I use it throughout my application to encrypt and decrypt localstorage items. While it works perfectly fine for the localstorage items, when I try to decrypt an encrypted object sent through queryparams, I get the following error:
core.js:478 Uncaught Error: Malformed UTF-8 data
my encrypt/decrypt service is:
export class AESEncryptDecryptService {
secretKey = 'My secret string';
constructor() { }
encrypt(value? : string) : string{
if(!isNil(value)) {
return CryptoJS.AES.encrypt(value, this.secretKey.trim()).toString();
}
}
decrypt(textToDecrypt?){
if(!isNil(textToDecrypt)) {
return CryptoJS.AES.decrypt(textToDecrypt, this.secretKey.trim()).toString(CryptoJS.enc.Utf8);
}
}
}
How I encrypt the object before I sent it:
const user= new User();
this.qrUrl = `${environment.someurl}` +'currentUser=' +this._AESEncryptDecryptService.encrypt(JSON.stringify(user).toString()).toString();
How I decrypt the object:
const url_string = window.location.href;
const url = new URL(url_string);
if(url.searchParams.get('user')) {
this.qrDevice = JSON.parse(this._AESEncryptDecryptService.decrypt(url.searchParams.get('user')));
}
I tried it without the string and by debugging. The same code works fine for other uses but gives this error on url query decrypt.
One thing I noticed is that the query string is replacing the + in the string with a space. How can I fix this and preserve the + sign? the expected and actual of the encrypted objects are posted below.
Expected after object parse from url:
U2FsdGVkX1+8y4FZ0cDq5ikapUndRA+tE5BAVqYPH9NnhBWeea1asYo5zCU80s/6FWKnFU8FghXv7JxPWwnPpJtCR+eXIGpiGBWq4gpq00PoeIuU2jPsDeifSu8aDrFr+D8abcdkIil5WmsHiND5TwVfWHhaBDSSlYMSXbiUXx9DQgRipEAtXXgMEO/r7G5wpuJ9ekEzUfkgXIO3eM/tP6dMu2iWZwbXTDvBZl93J8XZ259YRtIkRXgolSGS2t9yvQOn9I7fobRI1NSCIAftQtGdj/k9pu4B9reicnw9wiNR4dmp8+cpI/3TQSevhwp
Actual after object parse from url:
U2FsdGVkX1 8y4FZ0cDq5ikapUndRA tE5BAVqYPH9NnhBWeea1asYo5zCU80s/6FWKnFU8FghXv7JxPWwnPpJtCR eXIGpiGBWq4gpq00PoeIuU2jPsDeifSu8aDrFr D8abcdkIil5WmsHiND5TwVfWHhaBDSSlYMSXbiUXx9DQgRipEAtXXgMEO/r7G5wpuJ9ekEzUfkgXIO3eM/tP6dMu2iWZwbXTDvBZl93J8XZ259YRtIkRXgolSGS2t9yvQOn9I7fobRI1NSCIAftQtGdj/k9pu4B9reicnw9wiNR4dmp8 cpI/3TQSevhwp

Issue was resolved by changing the qrurl to the following:
this.qrUrl = `${environment.qrOrderURL}user=`+encodeURIComponent(this._AESEncryptDecryptService.encrypt(JSON.stringify(user)));

Related

Imported a public RSA key into nodejs

I am trying to transfer a public RSA key generated in swift into my Nodejs server. I generated the RSA key using the following code.
private var clientPriv: SecKey?
private var clientPub: SecKey?
private init(){
let params: [String: Any] = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): 4096
]
SecKeyGeneratePair(params as CFDictionary, &clientPub, &clientPriv)
}
I send the key to my server using this code
...
guard let clientPub = clientPub else { return }
let key = SecKeyCopyExternalRepresentation(clientPub, nil)! as Data
let pem = exportToPEM(data: key, withLabel: "PUBLIC KEY")
let data = ["clientPub": pem]
var urlRequest = URLRequest(url: url)
do {
try urlRequest.httpBody = JSONSerialization.data(withJSONObject: data)
urlRequest.httpMethod = "POST"
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}catch let err {
print(err)
}
let task = URLSession.shared.dataTask(with: urlRequest){ data, response, error in
guard let data = data, error == nil else {
return
}
...
The exportToPem helper looks like this.
public func exportToPEM(data: Data, withLabel label: String) -> String {
let key = data.base64EncodedString(options: [.lineLength64Characters])
var pem = "-----BEGIN \(label)-----\n"
pem += key
pem += "\n-----END \(label)-----\n"
return pem
}
On my Nodejs side, I am using express to handle my requests and body-parser to parse my json post data in requests. Here is what my Nodejs receiving code looks like.
app.post('/api/init', jsonParser, function (req, res) {
console.log(req.body.clientPub);
CLIENTPUB = crypto.createPublicKey({ key: req.body.clientPub, format: 'pem', type: 'pkcs1' });
console.log(CLIENTPUB);
res.write(JSON.stringify({'server-pub': SERVERPUB.toString()}));
res.end()
});
The problem is that the function crypto.createPublicKey keeps throwing an error, error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag. I have tried many different ways to write the string of my key but no matter what it seems that the crypto createPublicKey just refuses to take it. I have tried keeping the format with \n every 64 bytes or without \n at all, removing the header/footer altogether, and many other different combinations. I can not figure out why it keeps refusing to accept any format I send it. I have also tried using just the der format but that also gets refused.
Can anyone please offer me any advice on how to get this function to accept my key format?
SecKeyCopyExternalRepresentation() exports the public key in PKCS#1 format, which is correctly specified on the NodeJS side in the createPublicKey() call with 'pkcs1' for the type parameter.
However, the header and footer texts of a PEM encoded key for PKCS#1 format are BEGIN RSA PUBLIC KEY and END RSA PUBLIC KEY, so when calling exportToPEM(), "RSA PUBLIC KEY" must be passed in the second parameter instead of "PUBLIC KEY":
let pem = exportToPEM(data: key, withLabel: "RSA PUBLIC KEY")
BEGIN PUBLIC KEY and END PUBLIC KEY are used for a PEM encoded public key in X.509/SPKI format. This is what the error message wrong tag means.

Why does decrypting modified AES-CBC ciphertext fail decryption?

I am trying to get familiar with encryption/decryption. I am using deno as it supports the web crypto API.
I can encrypt and decrypt to get back the original plaintext using AES-CBC.
What I am now doing now is to encrypt, then manually modify the ciphertext and then decrypt. My expectation is that this would still work since I understand that AES-CBC does not provide integrity and authenticity check. (AES-GCM is the one that is AEAD)
But when I modify the cipher text and try to decrypt, it fails with the following error:
error: Uncaught (in promise) OperationError: Decryption failed
let deCryptedPlaintext = await window.crypto.subtle.decrypt(param, key, asByteArray);
^
at async SubtleCrypto.decrypt (deno:ext/crypto/00_crypto.js:598:29)
at async file:///Users/me/delete/run.js:33:26
Does AES-CBC also have integrity checks? Or why is the decryption failing?
In Deno I had a similar issue while encrypting a jwt around server and client and could not rely on the TextDecoder class for the same reason:
error: OperationError: Decryption failed
After some hours I played around and found a solution, a bit tricky, but is doing the job right:
(async ()=> {
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
const rawKey = crypto.getRandomValues(new Uint8Array(16));
// we import the key that we have previously generated
const cryptoKey = await crypto.subtle.importKey(
"raw",
rawKey,
"AES-CBC",
true,
["encrypt", "decrypt"],
);
// we generate the IV
const iv = crypto.getRandomValues(new Uint8Array(16));
// here is the string we want to encrypt
const stringToEncrypt = "foobar"
// we encrypt
const encryptedString = await crypto.subtle.encrypt(
{ name: "AES-CBC", iv: iv },
cryptoKey,
textEncoder.encode(stringToEncrypt),
);
// we transform the encrypted string to an UInt8Array
const uint8ArrayEncryptedString = new Uint8Array(encryptedString);
// we transform the Array to a String so we have a representation we can carry around
const stringifiedEncryption =
String.fromCharCode(...uint8ArrayEncryptedString);
/* now is time to decrypt again the message, so we transform the string into
a char array and for every iteration we transform
the char into a byte, so in the end we have a byte array
*/
const stringByteArray =
[...stringifiedEncryption].map((v) => v.charCodeAt(0))
// we transform the byte array into a Uint8Array buffer
const stringBuffer = new Uint8Array(stringByteArray.length);
// we load the buffer
stringByteArray.forEach((v, i) => stringBuffer[i] = v)
// we decrypt again
const againDecrString = await crypto.subtle.decrypt(
{ name: "AES-CBC", iv: iv },
cryptoKey,
stringBuffer,
);
console.log(textDecoder.decode(againDecrString))
})()
For some of you relying on the class for the same purpose, I suggest you to use this solution. The underlying implementation pheraps loses some information while converting back and forth the string (I needed it as string after the encryption) and so the decryption fails.

How to pass encripted data pass by url in angular 8

Tried to pass encripted data by url in angular 8 but not working because encripted data containing / so that url is not opening properly.So How to pass encripted data by url in angular 8.Please help anyone if know.
Can we encript the data without special chatacters.Is it possible?
Example: U2FsdGVkX18+ijuMYTf/b2jVWBRoAGWMJ+AnFlwodjE= this data have / so url will not open. How to resolve this issue.
Encription like this:
let encriptionData = CryptoJS.AES.encrypt("test#gmail.com", 'secret key 123').toString();
I am passing encripted data like this
var url = "http://localhost:4200/content/" + encriptionData;
router.module.ts:
{ path : 'content/:id' , component : contentComponent },
content.component.ts:
//url is http://localhost:4200/content/U2FsdGVkX18+ijuMYTf/b2jVWBRoAGWMJ+AnFlwodjE=
this._Activatedroute.paramMap.subscribe(params => {
var bytes = CryptoJS.AES.decrypt(params.get('id'), 'secret key 123');
this.decryptedMailid = bytes.toString(CryptoJS.enc.Utf8);
console.log(this.decryptedMailid);
});
You can encode your encrypted string, and then decode it before decrypting it.
let encryptedData = encodeURIComponent(CryptoJS.AES.encrypt("test#gmail.com", 'secret key 123').toString());
Now, instead of http://localhost:4200/content/U2FsdGVkX18+ijuMYTf/b2jVWBRoAGWMJ+AnFlwodjE=, your url will be http://localhost:4200/content/U2FsdGVkX18%2BijuMYTf%2Fb2jVWBRoAGWMJ%2BAnFlwodjE%3D
In content.component.ts, you do the following to reverse the operations:
this._Activatedroute.paramMap.subscribe(params => {
var bytes = CryptoJS.AES.decrypt(params.get('id'), 'secret key 123');
this.decryptedMailid = decodeURIComponent(bytes.toString(CryptoJS.enc.Utf8));
console.log(this.decryptedMailid);
});

How to fix "attribute xmlns:wsse invalid" error where calling SOAP web service in NodeJS using node-soap

I am trying to call a web service that requires authetication via X509 certificate. I'm using Node.js and node-soap module, getting successfully the WSDL.
But when I try to build the SOAP envelope with the Signature tag inside, I get the following error from the server:
The value of the attribute "prefix="xmlns",localpart="wsse",rawname="xmlns:wsse"" is invalid. Prefixed namespace bindings may not be empty.
I've tried to modify the existingPrefixes that are passed to the underlying library (xml-crypto).
This is my code:
var privateKey = fs.readFileSync(process.env.KEYFILE);
var publicKey = fs.readFileSync(process.env.CERTFILE);
var password = ''; // optional password
var options = {
mustUnderstand: 1,
signerOptions: {
existingPrefixes: {
'wsse': "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
}
},
};
var wsSecurity = new soap.WSSecurityCert(privateKey, publicKey, password, options);
const endpoint = process.env.ENDPOINT;
soap.createClientAsync(endpoint).then(client => {
client.setSecurity(wsSecurity);
return client.wiOperationAsync({ ... })
})
However, the error persists and I get a SOAP request body with something like this:
<KeyInfo>
<wsse:SecurityTokenReference
xmlns:wsse="">
<wsse:Reference URI="#x509-1639ca80191641b9bd804497cfcef0b5" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</KeyInfo>
Any idea to avoid that empty mlns:wsse attribute?

Prevent RestSharp changing https to http

I have a very simple piece of code using v130 of RestSharp. The code itself compiles fine and returns values from the server - the problem is they're not the ones I'm after.
Code
private void startTest()
{
string email = "bob2#example.com";
string password = "password";
doRestSharpWebRequest(email, password);
}
private void doRestSharpWebRequest(email, password)
{
var restClient = new RestClient();
restClient.BaseUrl = "https://mytestserver.com/api";
restClient.Authenticator = new SimpleAuthenticator("email", email, "password", password);
var restRequest = new RestRequest("token", Method.GET);
restRequest.AddHeader("Accepts", "application/json;version=1");
restRequest.AddHeader("Authorization", "apikey zyzyz");
restRequest.RequestFormat = DataFormat.Json;
IRestResponse<userlogin> response = restClient.Execute<userlogin>(restRequest);
}
where userlogin is just a class to deserialize to.
When I run the code, I'm presented with the following error
Message = No HTTP resource was found that matches the request URI 'http://mytestserver.com/api/token?Email=bob%40example.com&password=password'.
Two things are apparent, the # has been changed to %40 which should cause a problem and the http has been changed to https.
Can restsharp be told not to convert from https and http?

Resources