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.

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

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. These slight differences should not cause any problems when using standard UTF-8.

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

To load user data from a file: load(File file);

To save user data to a file: save(new UserData(String username, String password);

To get the default lastlogin file, use: new File(getMinecraftWorkingDir, "lastlogin");

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")); }