Decrypting forms authentication token (AES, SHA1) in node js - c#

Has anyone had luck decrypting a .NET forms authentication generated cookie in node js using the crypto library?
I'm using AES for encryption and SHA1 for validation in .NET forms authentication mechanism.
I was wondering if someone had this problem before and solved it?

I played with this and made the following code for Node.js:
var crypto = require('crypto');
var iconv = require('iconv-lite');
function hash(password) {
// your validation key from web.config + 00 at the end
var buf = new Buffer('...00', 'hex');
var hmac = crypto.createHmac('sha1', buf);
// convert password from utf8 to UTF16
var pass = iconv.encode(password, 'utf16le');
hmac.update(pass);
return hmac.digest('base64');
}
console.log(hash('test'));
Use it to compare hashes from your database.

It depends on web.config settings.
You can see the source code of Encryp and Decrypt here with all the different possibilities (Framework20SP1, Framework20SP2, etc)
https://github.com/Microsoft/referencesource/blob/master/System.Web/Security/FormsAuthentication.cs
For example, on Framework20SP1 the cookie looks like: Enc(IV+SerializedTicket+Modifier)+HMAC(Enc(IV+SerializedTicket+Modifier))

Related

Web service call with a SHA-384 ECDSA certificate

For the project I am working on I have to convert the following legacy code to use a SHA-384 ECDSA certificate, .NET Framework 4.8 as target.
Is it possible natively in C# or do I need some help like BouncyCastle lib and how?
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
[...]
// Certificate is in PKCS#12 format and contains a private key, certificate is password protected with the session id
var certificateBytes = Convert.FromBase64String(base64Certificate.ToString());
var certificate = new X509Certificate2();
certificate.Import(certificateBytes, sessionId, X509KeyStorageFlags.DefaultKeySet);
var result = (HttpWebRequest)WebRequest.Create( url );
result.Headers.Add( "x-jwt-authorization", $"Bearer {JSONWebToken}" );
result.ClientCertificates.Add( Certificate );
var response = (HttpWebResponse)request.GetResponse();
I tried to find a solution/explanation online but didn't work (keep getting a "Not supported" for the PrivateKey property when trying to import the certificate, and a 403 Forbidden as server response).
Also if I want to keep backward compatibility, how do I distinguish if the Base64 string contains a X509 or a SHA one to support both?

Secure SOAP request (with WSE 3.0?) C#

I need to sign and encrypt a SOAP request with a certificate to access a WS method, but i'm getting the same response all the time:
"A security error was encountered when verifying the message".
I guess there's something wrong with my code rather than any other issue.
Here it is:
cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(sCertificatePath, sCertificatePassword);
userToken = new Microsoft.Web.Services3.Security.Tokens.UsernameToken(sUser, sPass, Microsoft.Web.Services3.Security.Tokens.PasswordOption.SendHashed);
secureToken = new Microsoft.Web.Services3.Security.Tokens.X509SecurityToken(cert);
encDataToken = new Microsoft.Web.Services3.Security.EncryptedData(secureToken);
mSignUsernameToken = new Microsoft.Web.Services3.Security.MessageSignature(userToken);
mSignSecurityToken = new Microsoft.Web.Services3.Security.MessageSignature(secureToken);
wsVehicleInfo = new wsBusiness.VehicleInfoWSImplService();
vehData = new wsBusiness.getVehicleInfoRequest();
vehData.vehicleRegistration = "XXXXYYY";
vehData.language = "es";
requestContext = wsVehicleInfo.RequestSoapContext;
requestContext.Security.Elements.Add(encDataToken);
requestContext.Security.Tokens.Add(secureToken);
requestContext.Security.Elements.Add(mSignSecurityToken);
requestContext.Security.Timestamp.TtlInSeconds = 300;
requestContext.Security.Tokens.Add(userToken);
Is it correct? Actually I got some questions:
I'm signing and encrypting with the same certificate issued by a CA. Don't I need to encrypt with the server's one? How can I get it?
Does order of XML elements generated matter in the request? Which should be the code order?
The algorithm used in the documentation to encrypt the soap body is "aes128-gcm" but I wasn't able to find it and instead using "aes128-cbc". May it cause any trouble?
Is it recommended the usage of WSE in this case? I read this:
"Instead of asymmetrically encrypting the message, WSE use an asymmetric algorithm with a public copy of the recipient's X.509 certificate to encrypt the symmetric key that was actually used to encrypt the message data.". No way to just encrypt the SOAP body from the request with the server's certificate with WSE instead of encrypting the symmetric key?

How do you decode/decrypt a SignedCMS/PKCS#7 in C# PCL

So, I have an WebAPI that is returning a PKCS#7 file to a client. The client is written as a C# PCL so it can be used in Xamarin iOS and Android projects.
My initial tests worked fine because I was encoding and decoding in my unit tests and could use the Pkcs library. It seems I can't find any way of decoding the data on the client because I don't know of any Pkcs library that works with a PCL.
Can someone tell me how/if this can be done?
So, I did end up switching my project to .netstandard 1.4 and using Portable.BouncyCastle to decode the Cms created on the server side.
Here is the code that I used to decode the Cms. I'm sort of trusting that this is also checking the signature since there is no explicit method for doing that in BouncyCastle like there is via the framework code i.e. CheckSignature().
var cmsParser = new Org.BouncyCastle.Cms.CmsSignedDataParser(dataBytes);
var cmsSignedContent = cmsParser.GetSignedContent();
var contentStream = cmsSignedContent.ContentStream;
var memoryStream = new MemoryStream();
contentStream.CopyTo(memoryStream);
byte[] contentBytes = memoryStream.ToArray();
var decodedContent = Encoding.UTF8.GetString(contentBytes);
In addition I added this to verify the signer info:
cmsParser.GetSignedContent().Drain();
var certStore = cmsParser.GetCertificates("Collection");
var signerInfos = cmsParser.GetSignerInfos();
var signers = signerInfos.GetSigners();
foreach (SignerInformation signer in signers)
{
var certCollection = certStore.GetMatches(signer.SignerID);
foreach (Org.BouncyCastle.X509.X509Certificate cert in certCollection)
{
var result = signer.Verify(cert);
if (!result)
{
throw new Exception("Certificate verification error, the signer could not be verified.");
}
}
}
I'm not 100% sure if this is all I need to do but my client's will communicate via SSL and they are using an HMAC auth with an appId and client secret so I'm not so concerned with in transit issues. I'm basically transferring a "license" file and I want to make sure the contents are not tampered with after it has been saved on the client device.
If anyone has any suggestions or concerns with this please let me know.
Thanks.
I currently use PCLCrypto to encrypt and decrypt using PKCS#7 on the client (in a Xamarin Forms project). I assume it will do what you need as well.
The PKCS#7 wiki example can be found here

Generate a secret key for JWT?

Currently I have a hard-coded secret key I use for my JWT Token Generation. What is the best way to generate this randomly when generating the token? Also, what I don't understand is if the secret is randomly generated, how can it be that the secret would be randomly generated again for authentication purposes. Am I missing something here or am I way off on how this works? It appears that the secret key is not even random. Is it something I would store in web.config for example
Just expanding on #nodd13's post to I have used the following (in LinqPad) to randomly generate a key:
var key = new byte[32];
RNGCryptoServiceProvider.Create().GetBytes(key);
var base64Secret = Convert.ToBase64String(key);
// make safe for url
var urlEncoded = base64Secret.TrimEnd('=').Replace('+', '-').Replace('/', '_');
urlEncoded.Dump();
This is indeed random and as I understand it you only need to do this once and you can then store this in your web.config to be referenced later.
I used the following code from this blog post
var key = new byte[32];
RNGCryptoServiceProvider.Create().GetBytes(key);
var base64Secret = TextEncodings.Base64Url.Encode(key);

Is app.config file a secure place to store passwords?

I need to store confidential passwords within the code. I cannot use Hashing techniques as the password itself is needed. How can I store these data securely within an app.config file?
Are there other ways I could accomplish this securely?
DPAPI and ProtectData Class is not an option because the keys are system specific eg:connection strings can't be stored this way for different end user systems.
You can use DPAPI (Data protection API) to encrypt certain section of your config files. Your code would still be using ConfigurationManager and decrypting will be taken of care by the framework. For more information on the same refer to this patterns and practices document How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI
Update
To encrypt or decrypt information from your code you could use ProtectedData.Protect & ProtectedData.Unprotect. This can be run as a part of custom action in your installer or when the user enters the credentials when using your application.
Sample Code
class SecureStringManager
{
readonly Encoding _encoding = Encoding.Unicode;
public string Unprotect(string encryptedString)
{
byte[] protectedData = Convert.FromBase64String(encryptedString);
byte[] unprotectedData = ProtectedData.Unprotect(protectedData,
null, DataProtectionScope.CurrentUser);
return _encoding.GetString(unprotectedData);
}
public string Protect(string unprotectedString)
{
byte[] unprotectedData = _encoding.GetBytes(unprotectedString);
byte[] protectedData = ProtectedData.Protect(unprotectedData,
null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(protectedData);
}
}

Categories

Resources