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 8 Feb 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 paramspec = new IvParameterSpec(	                                                  new byte[] {	    (byte) 0xc0, (byte) 0x05, (byte) 0x86, (byte) 0x92, (byte) 0xec,	    (byte) 0x67, (byte) 0xcf, (byte) 0x39         }); private static final Cipher         cipher    = 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 { cipher.init(mode, key, paramspec); 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, cipher)		        : 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, cipher)		        : 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 + PyCrypto
PyCrypto usually comes packaged with python 2.7. To install it:
 * Ubuntu / Debian: $ sudo apt-get install python-crypto
 * Windows: Good luck, some legislation makes it illegal for the PyCrypto developers to provide pre-built binaries.

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 pkcs5_pad(data): padlen = DES.block_size - (len(data) % DES.block_size) return data + padlen * chr(padlen) def encode(data): writestr = lambda s: chr((len(s) >> 8) & 0xFF) + chr(len(s) & 0xFF) + s  writeutf = lambda u: writestr(u.encode("utf8")) return newcipher.encrypt(pkcs5_pad( writeutf(data[0]) + writeutf(data[1]) if not isinstance(data, basestring) else writeutf(data) + "\x0\x0"))

Example Usage
I have not yet devised a method to determine the minecraft directory in python. The variable "minecraft_appdir" below is assumed to equal the correct path.

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