Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

645
Vistas
How to decrypt a .NET Forms Authentication Cookie in Node.js

I am trying to see if its possible to decrypt a .NET Forms Authentication Cookie in Node.js

The cookie is generated via .NET Framework 4.6 If i check the machineKey value - it uses settings: decryptionKey="xxxxxxxxxxxxxxxx" validation="SHA1" decryption="DES"

And if i look at the .NET source i believe it defaults to the cbc des cipher: https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/mscorlib/system/security/cryptography/descryptoserviceprovider.cs#L90

With IV key length of 8 bytes and hmac size of 20 bytes

In the case of the ASPX cookie it has format: IV + DATA + HMAC

So if i try something like this below (which when decrypted should be a binary FormsAuthenticationTicket):

const COOKIE_CONTENTS = '86F1EDAAE112A4E56EB1DAA75411F07E8D82F648A87F13E8386735610....REDACTED FULL VALUE';
const decryptionKey = "xxxxxxxxxxxxxxxx";

const algorithm = 'des-cbc'; 
const key = Buffer.from(decryptionKey, "hex");

let cookie = COOKIE_CONTENTS;
let blob = Buffer.from(cookie, 'hex');
const ivSize = 8;
const hmacSize = 20;

let iv = blob.slice(0, ivSize);
let hmac = blob.slice(blob.length - hmacSize);
let encrypted = blob.slice(ivSize, blob.length - hmacSize);

console.log("Len (cookie):", cookie.length);
console.log("Len (blob):", blob.length);
console.log("IV:", iv, "len:", iv.length);
console.log("HMAC:", hmac, "len:", hmac.length);
console.log("Encrypted:", encrypted, "len:", encrypted.length);

const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = Buffer.from(decipher.update(encrypted, 'binary', 'binary') + decipher.final('binary'), 'binary'); 

And it works!

However when the token is generated with httpRuntime setting:

<httpRuntime targetFramework="4.5" enableVersionHeader="false" maxRequestLength="10240" />

I get a failure

error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

And unfortunately this is the auth tokens i need to decrypt as they are on staging and production systems.

This may be due to the way .NET has changed the way auth is done when you opt into targetFramework="4.5" It has .NET 4.5 “cryptographic improvements” See: https://devblogs.microsoft.com/dotnet/cryptographic-improvements-in-asp-net-4-5-pt-2/

I think the rundown is that:

  • “Purpose” is passed to the crypto routines that describe purpose, we need to provide the same string to decrypt it
  • Changes to how Message Authentication Code is stored (MAC)

Some of the .NET source code is referenced here: https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System.Web/Security/Cryptography/Purpose.cs

and also I can see the 4.5 setting block here: https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System.Web/Security/FormsAuthentication.cs#L155

and the code eventually ends up here: https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System.Web/Security/Cryptography/MachineKeyDataProtectorFactory.cs#L25-L29

but its hard to follow after that. Seems as though 4.5 mode bipasses the DES settings we have passed to it, so who knows what cipher its actually using when in this mode?

Unfortunately I cant just remove the targetFramework="4.5" part as this will mean all user tokens will fail after this is rolled out, meaning all users will need to login again which is not acceptable.

Does anyone know more details on how this can be done with a 4.5 “cryptographic improvements” token? Any ideas on what I am missing with these crypto settings - it would be great if this can be done in Node.js

UPDATE:

I have tried what @Sebastian has mentioned and tried to port over some of the python code without success (I believe i have written the node code correctly, please let me know if i'm missing something)

eg:

function writeUnsignedInt(v, buf, offset) {
  buf.writeInt32BE(v, offset);
}

// conversion of this: https://lowleveldesign.org/2014/11/11/decrypting-asp-net-identity-cookies/
function deriveKey(key, label, context, keyLengthInBits) {
  let labelCount = 0;
  let contextCount = 0;

  if (label) {
    labelCount = label.length;
  }

  if (context) {
    contextCount = context.length;
  }

  const buffer = Buffer.alloc((4 + labelCount + 1 + contextCount + 4));

  if (labelCount > 0) {
    buffer.write(label, 4, 'ascii');
  }

  if (contextCount > 0) {
    buffer.write(context, 5 + labelCount, 'ascii');
  }

  writeUnsignedInt(keyLengthInBits, buffer, 5 + labelCount + contextCount);

  let destOffset = 0;
  let value = parseInt(keyLengthInBits / 8, 10);

  let resultBuffer = Buffer.alloc(value);
  let num = 1;

  while(value > 0) {
    writeUnsignedInt(num, buffer, 0);

    var hmac = crypto.createHmac('sha512', key);
    let bufferString = buffer.toString();
    let hashedData = hmac.update(bufferString);

    let generatedHmac = hashedData.digest('hex');
    let count = Math.min(value, generatedHmac.length);
    resultBuffer.write(generatedHmac.substring(0, count), destOffset);

    destOffset += count;
    value -= count;
    num += 1;
  }

  return resultBuffer.toString();
}

const algorithm = 'des-cbc';

let key = Buffer.from(decryptionKey, "hex");
let blob = Buffer.from(cookie, 'hex');
const ivSize = 8;
const hmacSize = 20;

let iv = blob.slice(0, ivSize);
let hmac = blob.slice(blob.length - hmacSize);
let encrypted = blob.slice(ivSize, blob.length - hmacSize);

let dkey = deriveKey(key, 'FormsAuthentication.Ticket', '>Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware\x11ApplicationCookie\x02v1', 64);

const decipher = crypto.createDecipheriv(algorithm, dkey, iv);
let decrypted = Buffer.from(decipher.update(encrypted, 'binary', 'binary') + decipher.final('binary'), 'binary');

I think the main differences is:

  1. I need to deal with DES and not AES (as thats what has been set in the Web.Config file) with different IV and key sizes.
  2. Im dealing with a forms authentication ticket and not owin auth or anti forgery token.
  3. Im taking a stab at what the "label" is, im setting it as ">Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware\x11ApplicationCookie\x02v1" but not sure thats correct (cant find the source code for this)
  4. Im not sure I need to do any padding / base64 decoding of the encrypted data, because its in hex format (i basically get the DATA part from IV + DATA + HMAC and set it as a blob and pass to decipher.update)
over 4 years ago · Santiago Trujillo
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda