I'm writing a client for Minecraft in Java. I have a lot of things working already, but the one thing that has really stumped me is how Minecraft does its authentication. I found this page
http://wiki.vg/Protocol#Handshake_.280x02.29
Which defines the protocol. As of now, the following code
public boolean connect(String ip, int port) {
try {
socket = new Socket(ip, port);
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
dos.writeByte(0x02);
dos.writeByte(0x00);
writeString(username);
writeString(ip);
dos.writeInt(port);
if (dis.readByte() != 0xFD)
return false;
String serverId = readString();
byte[] publicKey = new byte[dis.readShort()];
for (int i = 0; i < publicKey.length; i++)
publicKey[i] = dis.readByte();
byte[] token = new byte[dis.readShort()];
for (int i = 0; i < token.length; i++)
token[i] = dis.readByte();
PublicKey serverPublicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey));
byte[] sharedSecret = new byte[16];
new Random().nextBytes(sharedSecret);
URL url = new URL("http://session.minecraft.net/game/joinserver.jsp?user=" + username + "&sessionId=" + session + "&serverId=" + serverId);
url.openConnection();
Cipher cipher = Cipher.getInstance("AES");
return true;
}
catch (Exception ex) { System.out.println("Failed to login for " + username); ex.printStackTrace(); }
return false;
}
is what I have. Through this code, as you can tell, I get a public key, a verification token, and generate a random shared secret. However, I don't know what to do from here. I have seen a Python implementation of this
https://github.com/ammaraskar/pyCraft/blob/master/networking/NetworkManager.py line 82
but I can't figure out how they used SHA1 to grab the serverid. I can't find an equivalent in Java. There is a authentication scheme page specific for Minecraft which more clearly defines what happens in the background here: http://wiki.vg/Protocol_Encryption
There is an excellent decompilation project for minecraft:
http://mcp.ocean-labs.de/index.php/MCP_Releases.
Have a look in src/minecraft/net/minecraft/src/NetClientHandler.java, and src/minecraft/net/minecraft/src/ThreadLoginVerifier.java.
The actual hashing is done in src/minecraft/net/minecraft/src/CryptManager.java, like this:
digestOperation("SHA-1", new byte[][] {par0Str.getBytes("ISO_8859_1"), par2SecretKey.getEncoded(), par1PublicKey.getEncoded()})
Related
I am trying to implement a basic Diffie-Hellman protocol and the code succeeds up to the point when it needs to decrypt the sent value using DES. I have looked at a lot of examples in which it was a matter of the keys not matching up, but I am printing their values on both ends of the connection and they are both exactly the same. I have also tried multiple padding schemes as well as changing how the keys were generated.
My last attempt was in adding the parameter IvParameterSpec to the cipher init, but that only solved one of the errors.
I am running this on a single machine where the socket is connecting over the localhost and I have been checking for any issues on either side with the sent data not exactly matching the received data, but nothing is altered in the sending. I did notice, however, that when printing each of the byte arrays on either side of the socket that the client side is much longer than the server with what appears to be padding(?)
The error I am getting is saying that the final block is padded incorrectly and so decryption fails
My Server code (the side that is not working as intended):
public static void main(String[] args) {
ServerSocket welcomeSocket = null;
// Creates a connectable socket on port 6789
try {
welcomeSocket = new ServerSocket(6789);
} catch (IOException e) {
e.printStackTrace();
}
while(true){
try{
double k2, B, A;
double n = 13;
double g = 61;
long y = 7;
B = (Math.pow(g, y))%n;
System.out.println("Accepting connections");
// Accept an incoming connection on the socket server
Socket connectionSocket = welcomeSocket.accept();
// Creates a read and write stream for that client
DataInputStream inFromClient = new DataInputStream(connectionSocket.getInputStream());
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
// Sends the double to the client
outToClient.writeDouble(B);
System.out.println("Sent " + B);
// Reads the number sent by the Client
A = inFromClient.readDouble();
System.out.println("Received " + A);
// Modifies the number
k2 = (Math.pow(A, y))%n;
System.out.println("DES key seed = " + k2);
byte[] deskeydata = toByteArray(k2);
// Turns the bytes of the modified number into a DES key spec
DESKeySpec deskeyspec = new DESKeySpec(deskeydata);
// Makes a secret key (DES)
SecretKeyFactory keyF = SecretKeyFactory.getInstance("DES");
SecretKey keystuff = keyF.generateSecret(deskeyspec);
System.out.println(keystuff.toString());
// Gets an incoming string from the client and turns it into binary
byte[] incomingBytes = new byte[128];
try{
inFromClient.readFully(incomingBytes);
} catch(EOFException eof){
System.out.println("Finished reading");
}
System.out.println(new String(incomingBytes));
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
// Decrypts the string using the shared secret key
c.init(Cipher.DECRYPT_MODE, keystuff, new IvParameterSpec(new byte[8]));
byte[] ct2 = c.doFinal(incomingBytes);
// Decode it from base 64
//byte[] decodedBytes = Base64.getDecoder().decode(ct2);
// Prints the received string
System.out.println("Received: " + new String(ct2));
inFromClient.close();
outToClient.close();
} catch(Exception e){
e.printStackTrace();
}
}
}
My Client code:
public static void main(String[] args) {
// Creates a socket to the local host on port 6789
Socket clientSocket = null;
try {
clientSocket = new Socket("localhost", 6789);
} catch (IOException e1) {
e1.printStackTrace();
}
try{
double k1, B, A;
double n = 13;
double g = 61;
long x = 3;
// Sends an unencrypted number to the server
A = (Math.pow(g, x))%n;
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
DataInputStream inFromServer = new DataInputStream(clientSocket.getInputStream());
// Transforms A into a byte array and sends it over
outToServer.writeDouble(A);
outToServer.flush();
System.out.println("Sending " + A);
// Reads the incoming data from the server
B = inFromServer.readDouble();
System.out.println("Recieved " + B);
// Modifies the data to create the number for des key
k1 = (Math.pow(B, x))%n;
System.out.println("DES key seed = " + k1);
byte[] deskeydata = toByteArray(k1);
// Turns the bytes of the modified number into a DES key spec
DESKeySpec deskeyspec = new DESKeySpec(deskeydata);
// Makes a secret key (DES)
SecretKeyFactory keyF = SecretKeyFactory.getInstance("DES");
SecretKey keystuff = keyF.generateSecret(deskeyspec);
System.out.println(keystuff.toString());
// Takes in input from the user and turns it into binary
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a message:");
String sentence = inFromUser.readLine();
byte[] str2 = sentence.getBytes();
byte[] encodedMessage = Base64.getEncoder().encode(str2);
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
// Encrypts the user's input with the secret key
c.init(Cipher.ENCRYPT_MODE, keystuff, new IvParameterSpec(new byte[8]));
byte[] ct2 = c.doFinal(encodedMessage);
System.out.println("Initted the cipher and moving forward with " + new String(ct2));
// Writes the encrypted message to the user
outToServer.write(ct2);
outToServer.flush();
inFromServer.close();
outToServer.close();
} catch(Exception e){
e.printStackTrace();
}
}
Anything that could help me in getting this working would be immensely welcome since I have been working on this error alone for quite some time.
I managed to figure out a way to solve this (though I suspect it is very inefficient). The issue was due to the readFully method on the server side. I was reading the answer into a 128 byte array and the decrypt function saw the empty slots in the byte array as something instead of nothing.
To solve this I replaced the input section with the following, which reads each individual byte and creates an array of bytes with the exact length of the incoming message.
ArrayList<Byte> totalBytes = new ArrayList<Byte>();
while(true){
try{
byte in = inFromClient.readByte();
totalBytes.add(in);
} catch(EOFException eof){
System.out.println("Finished reading");
break;
}
}
int incomingSize = totalBytes.size();
byte[] receivedBytes = new byte[incomingSize];
for(int i = 0; i < incomingSize; i++){
receivedBytes[i] = totalBytes.get(i);
}
I have my php application where when I created the user I ran this.
$random_salt = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));
// Create salted password
$userPwd = hash('sha512', $userPwd . $random_salt);
Next when I try to login upon the having captured the password I hash via this javascript
p.value = hex_sha512(userPwdControl.value);
Then in the ran this
$hashPassword = hash('sha512', $userPassword . $row1['userSalt']);
All above codes works via php.
Now via my android I want to do this function p.value = hex_sha512(userPwdControl.value); to get the hash and I am trying out first via java codes as below. But I got empty results below.
StringBuffer stringBuffer = new StringBuffer();
try {
String message = "myPass";
MessageDigest digest = MessageDigest.getInstance("512");
byte[] hashedBytes = digest.digest(message.getBytes("UTF-8"));
for (int i = 0; i < hashedBytes.length; i++) {
stringBuffer.append(Integer.toString((hashedBytes[i] & 0xff) + 0x100, 16)
.substring(1));
}
stringBuffer.toString();
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
}
System.out.println("TEST :"+stringBuffer);
Can you try the code below. Did some minor changes in your code.
String resultString = "";
try {
byte[] buffer = password.getBytes();
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(buffer);
byte[] digest = md.digest();
for(int i = 0 ; i < digest.length ; i++) {
int b = digest[i] & 0xff;
if(Integer.toHexString(b).length() == 1)
resultString = resultString + "0";
resultString = resultString + Integer.toHexString(b);
}
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
}
Your problem seems to be solved already, but I would strongly recommend to switch to a more safe hashing algorithm. A single SHA-512 cannot protect your users passwords because it is way too fast (1 Giga SHA-512 per second) and therefore can be brute-forced too easily.
What you need, is hash function with a cost factor, like BCrypt, PBKDF2 or SCrypt. PHPs function password_hash() currently implements BCrypt, a compatible implementation you can get with jBCrypt.
So I have been working with the Bouncycastle libraries in an attempt to connect with a remote server. This process has been problematic from the get go and now I'm close to getting everything working but some odd things are happening.
When I first started building out the encryption process I was told to use AES 256 with PKCS7Padding. After some nagging I was provided with a c++ example of the server code. It turned out that the IV is 256 bit so I had to use the RijndaelEngine instead. Also in order for this to work correctly I have to use ZeroBytePadding.
Here is my code:
socket = new Socket(remoteIP, port);
outputStream = new PrintWriter(socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
byte[] base_64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes("UTF-8");
Security.addProvider(new BouncyCastleProvider());
public String AESEncrypt(String out) throws IOException, DataLengthException, IllegalStateException, InvalidCipherTextException {
byte[] EncKey = key;
byte randKey;
Random randNumber = new Random();
randKey = base_64[randNumber.nextInt(base_64.length)];
EncKey[randKey&0x1f] = randKey;
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndaelEngine), new ZeroBytePadding());
ParametersWithIV keyParameter = new ParametersWithIV(new KeyParameter(EncKey), iv);
cipher.init(true, keyParameter);
byte[] txt = out.getBytes();
byte[] encoded = new byte[cipher.getOutputSize(txt.length)];
int len = cipher.processBytes(txt, 0, txt.length, encoded, 0);
cipher.doFinal(encoded, len);
char keyChar = (char) randKey;
String encString = new String(Base64.encode(encoded));
encString = encString.substring(0, encString.length()-1) + randKey;
return encString;
}
public void AESDecrypt(String in) throws DataLengthException, IllegalStateException, IOException, InvalidCipherTextException {
byte[] decKey = key;
byte[] msg = in.getBytes();
byte randKey = msg[msg.length-1];
decKey[randKey&0x1f] = randKey;
byte[] trimMsg = new byte[msg.length-1];
System.arraycopy(msg, 0, trimMsg, 0, trimMsg.length);
in = new String(trimMsg);
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndaelEngine), new ZeroBytePadding());
ParametersWithIV keyParameter = new ParametersWithIV(new KeyParameter(decKey), iv);
cipher.init(false, keyParameter);
byte[] encoded = Base64.decode(in.trim());
byte[] decoded = new byte[cipher.getOutputSize(encoded.length)];
int len = cipher.processBytes(encoded, 0, encoded.length, decoded, 0);
cipher.doFinal(decoded, len);
String decString = new String(decoded);
}
Here is a test function I am using to send and receive messages:
public void serverTest() throws DataLengthException, IllegalStateException, InvalidCipherTextException, IOException {
//out = AESEncrypt(out);
outputStream.write(out + "\n");
outputStream.flush();
String msg = "";
while ((msg = inputStream.readLine()) != null) {
AESDecrypt(msg);
}
}
The key and iv don't change with the exception of the last byte in the key. If I am encrypting I get a random base64 char and change the last byte to that. If its decryption I get the last byte from the message and set the last value of the key to it for decryption.
In the c++ example there was an unencrypted message and two encrypted messages. I could deal with those fine.
Here is the problem, when I send my message to the remote server "encrypted" the app waits for a response until the connection times out but never gets one. If I send the message unencrypted I get either 7 responses which I can successfully decrypt and finally
org.bouncycastle.util.encoders.DecoderException: unable to decode base64 string:
String index out of range: -4 at org.bouncycastle.util.encoders.Base64.decode(Unknown Source)
or my last line before the error will look like this:
?"??n?i???el????s???!_S=??ah????CR??l6??]?{?l??Y?????Gn???+?????9!'??gU&4>??{X????G?.$c=??0?5??GP???_Q5????8??Z\?~???<Kr?????[2\ ???a$?C??z%?W???{?.?????eR?j????~?B"$??"z??W;???<?Yu??Y*???Z?K?e!?????f?;O(?Zw0B??g<???????????,)?L>???A"?????<?????W??#\???f%??j ?EhY/?? ?5R?34r???#?1??I??????M
If I set the encryption/decryption to use PKCS7Padding I get no response when my message is encrypted still but with decryption from the server I get between 2 to 6 responses and then
org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted
I am at a loss with this. I don't know what I might be doing wrong so I have come here. I'm hoping the so community can point out my errors and guide me in the right direction.
I have a bit of an update I found my error in the encryption. I wasn't placing the random base64 value at the end of the encrypted string correctly so now I am doing like this.
encString += (char)randKey;
I can get response from the server now. Now the problem is I will some times get one or two readable lines but the rest are all garbage. I asked the individuals who run the server about it and they said in some c# code that they reference the have
return UTF8Encoding.UTF8.GetString(resultArray);
and thats all I have to go off of. I have tried UTF-8 encoding any place where I do getBytes or new String, and I have tried making the BurrferReader stream UTF-8 but it's still garbage.
Have you seedn the BCgit? this has bouncycastle code and examples. I am using the Csharp version in this repository. https://github.com/bcgit/bc-java
All crypto primitive examples are stored here: https://github.com/bcgit/bc-java/tree/master/core/src/test/java/org/bouncycastle/crypto/test
Try this code for testing Aes-CBC
private void testNullCBC()
throws InvalidCipherTextException
{
BufferedBlockCipher b = new BufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
b.init(true, new ParametersWithIV(kp, new byte[16]));
byte[] out = new byte[b.getOutputSize(tData.length)];
int len = b.processBytes(tData, 0, tData.length, out, 0);
len += b.doFinal(out, len);
if (!areEqual(outCBC1, out))
{
fail("no match on first nullCBC check");
}
b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
len = b.processBytes(tData, 0, tData.length, out, 0);
len += b.doFinal(out, len);
if (!areEqual(outCBC2, out))
{
fail("no match on second nullCBC check");
}
}
I've been using the Java security api and was following the tutorial on how to generate digital signatures: http://docs.oracle.com/javase/tutorial/security/apisign/gensig.html
The problem is when I run the code, the output signature is always different even when the private key and data which is to be signed stays constant. I have provided the code below which I used to run. "priv" is a file which holds the private key and is used to sign the data: "hello".
Basically the two outputs show the same data: "hello" signed with the same key but it gives a different output. I was expecting the same output to be given. Also when I run the program again, the signed data is different again from the initial run. Any help would be much appreciated. Thanks in advance
public static void main(String[] args) {
try {
FileInputStream privfis;
privfis = new FileInputStream("priv");
byte[] encKey = new byte[privfis.available()];
// obtain the encoded key in the file
privfis.read(encKey);
privfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec( encKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA", "SUN");
PrivateKey priv = keyFactory.generatePrivate(privKeySpec);
Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");
dsa.initSign(priv);
String message = "hello";
System.out.println(getHexString(message.getBytes()));
dsa.update(message.getBytes());
byte[] realSig = dsa.sign();
System.out.println(getHexString(realSig));
dsa.update(message.getBytes());
System.out.println(getHexString(message.getBytes()));
byte[] realSig2 = dsa.sign();
System.out.println(getHexString(realSig2));
} catch (Exception e) {
System.err.println("Caught exception " + e.toString());
e.printStackTrace();
}
}
private static String getHexString(byte[] b) {
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am making an app in which I have to encrypt the xml from my side and send it to server and in response I will receive xml and I have to decrypt it. I have no idea to encrypt and decrypt. My code is as follows
<?xml version='1.0' encoding='utf-8'?><adm_auth_req><user_name>user.s7</user_name><password>gspcsmo</password></adm_auth_req>
I am using this code to encrypt and decrypt it
public string encryptData(string key, string data)
{
int keyLen = key.Length;
int dataLen = Convert.ToInt16(data.Length);
char chData;
char chKey;
char[] data1 = data.ToCharArray();
char[] key1 = key.ToCharArray();
StringBuilder encryptedData = new StringBuilder();
for (int i = 0; i < dataLen; i++)
{
chData = data1[i];
for (int j = 0; j < keyLen; j++)
{
chKey = key1[j];
chData = (char)(chData ^ chKey);
}
encryptedData.Append(chData);
}
return (encryptedData.ToString());
}
But still all in vain. Can anyone tell me how to encrypt it and decrypt the result?
What is the problem you're solving?
Maybe SSL is matching you?
Encryption out of the box, standart solution.
Also you can take a look at JCA. But i think, it will be too heavy solution for your problem.
In my opinion, you should not try to implement a custom algorithm as first off, you're reinventing the wheel, and second off it will probably be no where near as secure as other more standard encryption routines. If I were you, I would take a look around for some good Java Encryption libraries. One I found is here, http://www.bouncycastle.org/latest_releases.html
I have used DES algorith for encryption and decryption.
For encryption:Here after encryption, I am writing file to save. You can save it with other(temp) name and send it to server. After successful sending you can delete this encrypted file
FileOutputStream fos = null ;
CipherInputStream cis;
byte key[] = "abcdEFGH".getBytes();
SecretKeySpec secretKey = new SecretKeySpec(key,"DES");
Cipher encrypt = Cipher.getInstance("DES/ECB/PKCS5Padding");
encrypt.init(Cipher.ENCRYPT_MODE, secretKey);
InputStream fis = new ByteArrayInputStream(fileData);//Here I am getting file data as byte array. You can convert your file data to InputStream by other way too.
File dataFile = new File(dataDir,fileName); //dataDir is location where my file is stored
if(!dataFile.exists()){
cis = new CipherInputStream(fis,encrypt);
try {
fos = new FileOutputStream(dataFile);
byte[] b = new byte[8];
int i;
while ((i=cis.read(b)) != -1) {
fos.write(b, 0, i);
}
return fileName;
} finally{
try {
if(fos != null)
{
fos.flush();
fos.close();
}
cis.close();
fis.close();
} catch (IOException e) {
//IOException
}
}
}
return "";
For decryption:
CipherInputStream cis;
FileOutputStream fos = null;
FileInputStream fis = null;
File dataFile = new File(dataDir,fileName); // here I am getting encrypted file from server
File newDataFile = new File(dataDir,fileName+"_TEMP"); // I am creating temporary decrypted file
byte key[] = "abcdEFGH".getBytes();
SecretKeySpec secretKey = new SecretKeySpec(key,"DES");
Cipher decrypt = Cipher.getInstance("DES/ECB/PKCS5Padding");
decrypt.init(Cipher.DECRYPT_MODE, secretKey);
try {
fis = new FileInputStream(dataFile);
} catch(Exception e) {
//Exception
}
if(dataFile.exists()){
cis = new CipherInputStream(fis,decrypt);
try {
fos = new FileOutputStream(newDataFile);
byte[] b = new byte[8];
int i;
while ((i=cis.read(b)) != -1) {
fos.write(b, 0, i);
}
return newDataFile;
} finally{
try {
if(fos != null)
{
fos.flush();
fos.close(); }
cis.close();
fis.close();
} catch (IOException e) {
//IOException
}
}
}
There are already some answers on SO which might fit you answer.
Encrypt and decrypt a String in java
How to encrypt String in Java
Why dont you use Twofish for that, XML is Text and all you need to use is the algorithm and you can find alot of examples for that.