forked from GeyserMC/Geyser
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch Floodgate encryption from RSA to AES
- Loading branch information
Showing
10 changed files
with
445 additions
and
124 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
common/src/main/java/org/geysermc/floodgate/crypto/AesCipher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
* | ||
* @author GeyserMC | ||
* @link https://github.com/GeyserMC/Floodgate | ||
* | ||
*/ | ||
|
||
package org.geysermc.floodgate.crypto; | ||
|
||
import org.geysermc.floodgate.util.InvalidHeaderException; | ||
|
||
import javax.crypto.Cipher; | ||
import javax.crypto.SecretKey; | ||
import javax.crypto.spec.GCMParameterSpec; | ||
import java.nio.ByteBuffer; | ||
import java.security.Key; | ||
import java.security.SecureRandom; | ||
|
||
public final class AesCipher implements FloodgateCipher { | ||
private static final int IV_LENGTH = 12; | ||
private static final int TAG_BIT_LENGTH = 128; | ||
private static final String CIPHER_NAME = "AES/GCM/NoPadding"; | ||
|
||
private final SecureRandom secureRandom = new SecureRandom(); | ||
private SecretKey secretKey; | ||
|
||
public void init(Key key) { | ||
if (!"AES".equals(key.getAlgorithm())) { | ||
throw new RuntimeException( | ||
"Algorithm was expected to be AES, but got " + key.getAlgorithm() | ||
); | ||
} | ||
secretKey = (SecretKey) key; | ||
} | ||
|
||
public byte[] encrypt(byte[] data) throws Exception { | ||
Cipher cipher = Cipher.getInstance(CIPHER_NAME); | ||
|
||
byte[] iv = new byte[IV_LENGTH]; | ||
secureRandom.nextBytes(iv); | ||
|
||
GCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_LENGTH, iv); | ||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec); | ||
byte[] cipherText = cipher.doFinal(data); | ||
|
||
return ByteBuffer.allocate(iv.length + cipherText.length + HEADER_LENGTH) | ||
.put(IDENTIFIER).put(VERSION) // header | ||
.put(iv) | ||
.put(cipherText) | ||
.array(); | ||
} | ||
|
||
public byte[] decrypt(byte[] cipherTextWithIv) throws Exception { | ||
HeaderResult pair = checkHeader(cipherTextWithIv); | ||
if (pair.getVersion() != VERSION) { | ||
throw new InvalidHeaderException( | ||
"Expected version " + VERSION + ", got " + pair.getVersion() | ||
); | ||
} | ||
|
||
Cipher cipher = Cipher.getInstance(CIPHER_NAME); | ||
|
||
int bufferLength = cipherTextWithIv.length - HEADER_LENGTH; | ||
ByteBuffer buffer = ByteBuffer.wrap(cipherTextWithIv, HEADER_LENGTH, bufferLength); | ||
|
||
byte[] iv = new byte[IV_LENGTH]; | ||
buffer.get(iv); | ||
|
||
byte[] cipherText = new byte[buffer.remaining()]; | ||
buffer.get(cipherText); | ||
|
||
GCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_LENGTH, iv); | ||
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec); | ||
return cipher.doFinal(cipherText); | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
common/src/main/java/org/geysermc/floodgate/crypto/AesKeyProducer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
* | ||
* @author GeyserMC | ||
* @link https://github.com/GeyserMC/Floodgate | ||
* | ||
*/ | ||
|
||
package org.geysermc.floodgate.crypto; | ||
|
||
import javax.crypto.KeyGenerator; | ||
import javax.crypto.SecretKey; | ||
import javax.crypto.spec.SecretKeySpec; | ||
import java.security.SecureRandom; | ||
|
||
public final class AesKeyProducer implements KeyProducer { | ||
public static int KEY_SIZE = 128; | ||
|
||
@Override | ||
public SecretKey produce() { | ||
try { | ||
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); | ||
keyGenerator.init(KEY_SIZE, SecureRandom.getInstanceStrong()); | ||
return keyGenerator.generateKey(); | ||
} catch (Exception exception) { | ||
throw new RuntimeException(exception); | ||
} | ||
} | ||
|
||
@Override | ||
public SecretKey produceFrom(byte[] keyFileData) { | ||
try { | ||
return new SecretKeySpec(keyFileData, "AES"); | ||
} catch (Exception exception) { | ||
throw new RuntimeException(exception); | ||
} | ||
} | ||
} |
165 changes: 165 additions & 0 deletions
165
common/src/main/java/org/geysermc/floodgate/crypto/FloodgateCipher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
* | ||
* @author GeyserMC | ||
* @link https://github.com/GeyserMC/Floodgate | ||
* | ||
*/ | ||
|
||
package org.geysermc.floodgate.crypto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import org.geysermc.floodgate.util.InvalidHeaderException; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.security.Key; | ||
|
||
/** | ||
* Responsible for both encrypting and decrypting data | ||
*/ | ||
public interface FloodgateCipher { | ||
byte[] IDENTIFIER = "Floodgate".getBytes(StandardCharsets.UTF_8); | ||
byte VERSION = 2; | ||
|
||
int HEADER_LENGTH = IDENTIFIER.length + 1; // one byte for version | ||
|
||
/** | ||
* Initializes the instance by giving it the key it needs to encrypt or decrypt data | ||
* | ||
* @param key the key used to encrypt and decrypt data | ||
*/ | ||
void init(Key key); | ||
|
||
/** | ||
* Encrypts the given data using the Key provided in {@link #init(Key)} | ||
* | ||
* @param data the data to encrypt | ||
* @return the encrypted data | ||
* @throws Exception when the encryption failed | ||
*/ | ||
byte[] encrypt(byte[] data) throws Exception; | ||
|
||
/** | ||
* Encrypts data from a String.<br> | ||
* This method internally calls {@link #encrypt(byte[])} | ||
* | ||
* @param data the data to encrypt | ||
* @return the encrypted data | ||
* @throws Exception when the encryption failed | ||
*/ | ||
default byte[] encryptFromString(String data) throws Exception { | ||
return encrypt(data.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
/** | ||
* Decrypts the given data using the Key provided in {@link #init(Key)} | ||
* | ||
* @param data the data to decrypt | ||
* @return the decrypted data | ||
* @throws Exception when the decrypting failed | ||
*/ | ||
byte[] decrypt(byte[] data) throws Exception; | ||
|
||
/** | ||
* Decrypts a byte[] and turn it into a String.<br> | ||
* This method internally calls {@link #decrypt(byte[])} | ||
* and converts the returned byte[] into a String. | ||
* | ||
* @param data the data to encrypt | ||
* @return the decrypted data in a UTF-8 String | ||
* @throws Exception when the decrypting failed | ||
*/ | ||
default String decryptToString(byte[] data) throws Exception { | ||
byte[] decrypted = decrypt(data); | ||
if (decrypted == null) { | ||
return null; | ||
} | ||
return new String(decrypted, StandardCharsets.UTF_8); | ||
} | ||
|
||
/** | ||
* Decrypts a String.<br> | ||
* This method internally calls {@link #decrypt(byte[])} | ||
* by converting the UTF-8 String into a byte[] | ||
* | ||
* @param data the data to decrypt | ||
* @return the decrypted data in a byte[] | ||
* @throws Exception when the decrypting failed | ||
*/ | ||
default byte[] decryptFromString(String data) throws Exception { | ||
return decrypt(data.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
/** | ||
* Checks if the header is valid and return a IntPair containing the header version | ||
* and the index to start reading the actual encrypted data from. | ||
* | ||
* @param data the data to check | ||
* @return IntPair. x = version number, y = the index to start reading from. | ||
* @throws InvalidHeaderException when the header is invalid | ||
*/ | ||
default HeaderResult checkHeader(byte[] data) throws InvalidHeaderException { | ||
final int identifierLength = IDENTIFIER.length; | ||
|
||
if (data.length <= HEADER_LENGTH) { | ||
throw new InvalidHeaderException("Data length is smaller then header." + | ||
"Needed " + HEADER_LENGTH + ", got " + data.length); | ||
} | ||
|
||
for (int i = 0; i < identifierLength; i++) { | ||
if (IDENTIFIER[i] != data[i]) { | ||
StringBuilder receivedIdentifier = new StringBuilder(); | ||
for (byte b : IDENTIFIER) { | ||
receivedIdentifier.append(b); | ||
} | ||
|
||
throw new InvalidHeaderException(String.format( | ||
"Expected identifier %s, got %s", | ||
new String(IDENTIFIER, StandardCharsets.UTF_8), | ||
receivedIdentifier.toString() | ||
)); | ||
} | ||
} | ||
|
||
return new HeaderResult(data[identifierLength], HEADER_LENGTH); | ||
} | ||
|
||
static boolean hasHeader(String data) { | ||
if (data.length() < IDENTIFIER.length) { | ||
return false; | ||
} | ||
|
||
for (int i = 0; i < IDENTIFIER.length; i++) { | ||
if (IDENTIFIER[i] != data.charAt(i)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
@Data | ||
@AllArgsConstructor | ||
class HeaderResult { | ||
private int version; | ||
private int startIndex; | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
common/src/main/java/org/geysermc/floodgate/crypto/KeyProducer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
* | ||
* @author GeyserMC | ||
* @link https://github.com/GeyserMC/Floodgate | ||
* | ||
*/ | ||
|
||
package org.geysermc.floodgate.crypto; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.security.Key; | ||
|
||
public interface KeyProducer { | ||
Key produce(); | ||
Key produceFrom(byte[] keyFileData); | ||
|
||
default Key produceFrom(Path keyFileLocation) throws IOException { | ||
return produceFrom(Files.readAllBytes(keyFileLocation)); | ||
} | ||
} |
Oops, something went wrong.