mirror of
https://github.com/Athou/commafeed.git
synced 2026-10-09 22:05:30 +00:00
use a secure algorithm to generate image urls so that only server-side generated urls can be used
This commit is contained in:
@@ -1,37 +1,68 @@
|
|||||||
package com.commafeed.backend.feed;
|
package com.commafeed.backend.feed;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.utils.Base64;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.GCMParameterSpec;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class ImageProxyUrl {
|
public class ImageProxyUrl {
|
||||||
|
|
||||||
|
private static final SecretKey KEY;
|
||||||
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
private static final int GCM_IV_LENGTH = 12;
|
||||||
|
private static final int GCM_TAG_LENGTH = 128;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
||||||
|
keyGen.init(256, SECURE_RANDOM);
|
||||||
|
KEY = keyGen.generateKey();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Failed to generate AES key", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String encode(String url) {
|
public static String encode(String url) {
|
||||||
return Base64.encodeBase64String(rot13(url).getBytes());
|
try {
|
||||||
|
byte[] iv = new byte[GCM_IV_LENGTH];
|
||||||
|
SECURE_RANDOM.nextBytes(iv);
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, KEY, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
|
||||||
|
byte[] encrypted = cipher.doFinal(url.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
byte[] combined = Bytes.concat(iv, encrypted);
|
||||||
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(combined);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Failed to encode URL", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String decode(String code) {
|
public static String decode(String code) {
|
||||||
return rot13(new String(Base64.decodeBase64(code)));
|
try {
|
||||||
}
|
byte[] combined = Base64.getUrlDecoder().decode(code);
|
||||||
|
|
||||||
private static String rot13(String msg) {
|
byte[] iv = Arrays.copyOfRange(combined, 0, GCM_IV_LENGTH);
|
||||||
StringBuilder message = new StringBuilder();
|
byte[] encrypted = Arrays.copyOfRange(combined, GCM_IV_LENGTH, combined.length);
|
||||||
|
|
||||||
for (char c : msg.toCharArray()) {
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||||
if (c >= 'a' && c <= 'm') {
|
cipher.init(Cipher.DECRYPT_MODE, KEY, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
|
||||||
c += 13;
|
byte[] decrypted = cipher.doFinal(encrypted);
|
||||||
} else if (c >= 'n' && c <= 'z') {
|
|
||||||
c -= 13;
|
|
||||||
} else if (c >= 'A' && c <= 'M') {
|
|
||||||
c += 13;
|
|
||||||
} else if (c >= 'N' && c <= 'Z') {
|
|
||||||
c -= 13;
|
|
||||||
}
|
|
||||||
message.append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return message.toString();
|
return new String(decrypted, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Failed to decode URL", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user