User:Higgs1/lastlogin

The lastlogin file contains the username and the password of the last user to use the launcher to log into Minecraft. The password is left blank if the user did not choose to have the launcher remember it. The file is usually encrypted.

File Format & Encryption
The lastlogin file is encrypted using DES in CBC mode with PKCS #5 padding, with the key and IV provided below. if the user's Java platform does not support this method of encrpytion the username and password are stored without any encryption. This information has last been verified as correct on 12 Mar 2013.

Key: 0xd80877e761acd91d IV: 0xc0058692ec67cf39

These encryption methods will not be explained in this document, and you should use your selected programming language's native implementations. All major Java implementations support DES. Bouncy Castle provides a free Java (and C#) implementation for all the required cryptography components.

The decrypted file format is the username followed by the password, both stored in a slightly modified version of UTF-8 with each field's length stored as an unsigned short before each string. These slight differences should not cause any problems when using standard UTF-8.

Implementations
I hereby release all of my rights to the code contained within this this page.

Java + JCE
Cipher.getInstance is expensive, so here it is only called once and the result is stored as a static final.

import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec;

public class LastloginCodec { public static class UserData { public String username; public String password; public UserData(final String username, final String password) { this.username = username; this.password = password; }   }    private static final Key             key = new SecretKeySpec(new byte[] {            (byte) 0xd8, (byte) 0x08, (byte) 0x77, (byte) 0xe7, (byte) 0x61,            (byte) 0xac, (byte) 0xd9, (byte) 0x1d }, "DES"); private static final IvParameterSpec iv = new IvParameterSpec(new byte[] {            (byte) 0xc0, (byte) 0x05, (byte) 0x86, (byte) 0x92, (byte) 0xec,            (byte) 0x67, (byte) 0xcf, (byte) 0x39 }); private static final Cipher         cip = getCipher; private static final Cipher getCipher { try { return Cipher.getInstance("DES/CBC/PKCS5Padding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {} return null; }   private static synchronized boolean initCipher(final int mode) { try { cip.init(mode, key, iv); return true; } catch (InvalidKeyException | InvalidAlgorithmParameterException           | NullPointerException e) {} return false; }   public static synchronized UserData load(final InputStream is) throws IOException { @SuppressWarnings("resource") final DataInputStream dis = new DataInputStream(           initCipher(Cipher.DECRYPT_MODE) ? new CipherInputStream(is, cip)                : is); final UserData data = new UserData(dis.readUTF, dis.readUTF); dis.close; return data; }   public static synchronized void save(final OutputStream os,        final UserData data) throws IOException { final DataOutputStream dos = new DataOutputStream(           initCipher(Cipher.ENCRYPT_MODE) ? new CipherOutputStream(os, cip)                : os); dos.writeUTF(data.username); dos.writeUTF(data.password); dos.close; } }

Example Usage
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;

public static void main(final String[] args) throws FileNotFoundException, IOException { File lastlogin = new File(getMinecraftWorkingDir, "lastlogin"); // Read data from lastlogin file. final LastloginCodec.UserData data = LastloginCodec .load(new FileInputStream(lastlogin)); System.out.println(data.username); System.out.println(data.password); // Write data to lastlogin file. LastloginCodec.save(new FileOutputStream(lastlogin), 	   new LastloginCodec.UserData("Username", "Password")); }

Python 2.7
Choose between one of the following DES libraries: pyDES and PyCrypto.

Using pyDES
Install pyDES using the easy_install package: pydes

from pyDes import des, CBC, PAD_PKCS5 def newcipher: return des(    mode = CBC, padmode = PAD_PKCS5,     key  = "\xd8\x08w\xe7a\xac\xd9\x1d",     IV   = "\xc0\x05\x86\x92\xecg\xcf9") def decode(data): data   = newcipher.decrypt(data) userend = (ord(data[0]) << 8) + ord(data[1]) + 2 passend = (ord(data[userend]) << 8) + ord(data[userend + 1]) + userend + 2 return (unicode(data[2: userend]), unicode(data[userend + 2: passend])) def writeutf(ustr): str   = ustr.encode("utf") strlen = len(str) return chr((strlen >> 8) & 0xFF) + chr(strlen & 0xFF) + str def encode(data, encrypt = True): data = (writeutf(data[0]) + writeutf(data[1])    if not isinstance(data, basestring)     else writeutf(data) + "\0\0") return (newcipher.encrypt(data)    if encrypt else data)

Using PyCrypto
PyCrypto usually comes packaged with python 2.7 in many Linux distros. To install it in other environments: PyCrypto does not include a PKCS5 padding function, but it only takes 2 lines to implement.
 * easy_install package (preferred): pycrypto
 * Ubuntu / Debian: $ sudo apt-get install python-crypto
 * Windows: Good luck because United States + Canada legislation makes it illegal for the PyCrypto developer(s) to provide pre-built binaries. (Somebody with windows knowledge please add actual useful instructions, thanks)

from Crypto.Cipher import DES def newcipher: return DES.new(    mode = DES.MODE_CBC,      key  = "\xd8\x08w\xe7a\xac\xd9\x1d",     IV   = "\xc0\x05\x86\x92\xecg\xcf9") def decode(data): data   = newcipher.decrypt(data) userend = (ord(data[0]) << 8) + ord(data[1]) + 2 passend = (ord(data[userend]) << 8) + ord(data[userend + 1]) + userend + 2 return (unicode(data[2: userend]), unicode(data[userend + 2: passend])) def writeutf(ustr): str   = ustr.encode("utf") strlen = len(str) return chr((strlen >> 8) & 0xFF) + chr(strlen & 0xFF) + str def pkcs5_pad(data): padlen = DES.block_size - (len(data) % DES.block_size) return data + padlen * chr(padlen) def encode(data, encrypt = True): data = (writeutf(data[0]) + writeutf(data[1])    if not isinstance(data, basestring)     else writeutf(data) + "\0\0") return (newcipher.encrypt(pkcs5_pad(data))    if encrypt else data)

Example Usage
The user data is stored as a string pair in the format of (username, password).

I have not yet devised a method to determine the minecraft directory in python. The variable "minecraft_lastlogin" below is assumed to equal the correct path of the lastlogin file.

userdata = decode(open(minecraft_lastlogin, "rb").read) open(minecraft_lastlogin, "wb").write(encode(("Username", "Password"))) open(minecraft_lastlogin, "wb").write(encode("Username"))
 * 1) Read data from lastlogin file.
 * 1) Write data to lastlogin file.
 * 1) Write username only to lastlogin file.

Bash + OpenSSL
Decoding: openssl des-cbc -d -in ~/.minecraft/lastlogin -K d80877e761acd91d -iv c0058692ec67cf39 \ 2> /dev/null | strings

Javascript
Coming soon... see User:Higgs/mclljs