Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

646
Visualizações
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 à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda