2018-09-17 10:20:34 +03:00
|
|
|
package ru.gravit.utils.helper;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.security.InvalidKeyException;
|
|
|
|
import java.security.Key;
|
|
|
|
import java.security.KeyFactory;
|
|
|
|
import java.security.KeyPair;
|
|
|
|
import java.security.KeyPairGenerator;
|
|
|
|
import java.security.MessageDigest;
|
|
|
|
import java.security.NoSuchAlgorithmException;
|
|
|
|
import java.security.SecureRandom;
|
|
|
|
import java.security.Signature;
|
|
|
|
import java.security.SignatureException;
|
|
|
|
import java.security.interfaces.RSAKey;
|
|
|
|
import java.security.interfaces.RSAPrivateKey;
|
|
|
|
import java.security.interfaces.RSAPublicKey;
|
|
|
|
import java.security.spec.InvalidKeySpecException;
|
|
|
|
import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
|
import java.security.spec.X509EncodedKeySpec;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Random;
|
|
|
|
|
|
|
|
import javax.crypto.Cipher;
|
2018-10-08 16:57:29 +03:00
|
|
|
import javax.crypto.KeyGenerator;
|
2018-09-17 10:07:32 +03:00
|
|
|
import javax.crypto.NoSuchPaddingException;
|
2018-10-08 16:57:29 +03:00
|
|
|
import javax.crypto.SecretKey;
|
|
|
|
import javax.crypto.spec.SecretKeySpec;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
import ru.gravit.launcher.LauncherAPI;
|
|
|
|
|
|
|
|
public final class SecurityHelper {
|
2018-11-08 15:30:16 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public enum DigestAlgorithm {
|
|
|
|
PLAIN("plain", -1), MD5("MD5", 128), SHA1("SHA-1", 160), SHA224("SHA-224", 224), SHA256("SHA-256", 256), SHA512("SHA-512", 512);
|
|
|
|
private static final Map<String, DigestAlgorithm> ALGORITHMS;
|
|
|
|
|
|
|
|
static {
|
|
|
|
DigestAlgorithm[] algorithmsValues = values();
|
|
|
|
ALGORITHMS = new HashMap<>(algorithmsValues.length);
|
|
|
|
for (DigestAlgorithm algorithm : algorithmsValues)
|
2018-09-22 17:33:00 +03:00
|
|
|
ALGORITHMS.put(algorithm.name, algorithm);
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static DigestAlgorithm byName(String name) {
|
|
|
|
return VerifyHelper.getMapValue(ALGORITHMS, name, String.format("Unknown digest algorithm: '%s'", name));
|
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
// Instance
|
|
|
|
public final String name;
|
|
|
|
|
|
|
|
public final int bits;
|
|
|
|
|
|
|
|
public final int bytes;
|
|
|
|
|
|
|
|
DigestAlgorithm(String name, int bits) {
|
|
|
|
this.name = name;
|
|
|
|
this.bits = bits;
|
|
|
|
|
|
|
|
// Convert to bytes
|
|
|
|
bytes = bits / Byte.SIZE;
|
|
|
|
assert bits % Byte.SIZE == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] verify(byte[] digest) {
|
|
|
|
if (digest.length != bytes)
|
2018-09-22 17:33:00 +03:00
|
|
|
throw new IllegalArgumentException("Invalid digest length: " + digest.length);
|
2018-09-17 10:07:32 +03:00
|
|
|
return digest;
|
|
|
|
}
|
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
// Algorithm constants
|
2018-11-08 15:30:16 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final String RSA_ALGO = "RSA";
|
2018-11-08 15:30:16 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final String RSA_SIGN_ALGO = "SHA256withRSA";
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final String RSA_CIPHER_ALGO = "RSA/ECB/PKCS1Padding";
|
|
|
|
// Algorithm size constants
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final int TOKEN_LENGTH = 16;
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-10-18 18:35:16 +03:00
|
|
|
public static final int AES_KEY_LENGTH = 8;
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final int TOKEN_STRING_LENGTH = TOKEN_LENGTH << 1;
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final int RSA_KEY_LENGTH_BITS = 2048;
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final int RSA_KEY_LENGTH = RSA_KEY_LENGTH_BITS / Byte.SIZE;
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final int CRYPTO_MAX_LENGTH = 2048;
|
|
|
|
// Certificate constants
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final String HEX = "0123456789abcdef";
|
2018-10-08 16:57:29 +03:00
|
|
|
public static final byte[] NUMBERS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
2018-09-17 10:07:32 +03:00
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static final SecureRandom secureRandom = new SecureRandom();
|
|
|
|
// Random generator constants
|
|
|
|
private static final char[] VOWELS = {'e', 'u', 'i', 'o', 'a'};
|
|
|
|
|
|
|
|
private static final char[] CONS = {'r', 't', 'p', 's', 'd', 'f', 'g', 'h', 'k', 'l', 'c', 'v', 'b', 'n', 'm'};
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] digest(DigestAlgorithm algo, byte[] bytes) {
|
|
|
|
return newDigest(algo).digest(bytes);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] digest(DigestAlgorithm algo, InputStream input) throws IOException {
|
|
|
|
byte[] buffer = IOHelper.newBuffer();
|
|
|
|
MessageDigest digest = newDigest(algo);
|
|
|
|
for (int length = input.read(buffer); length != -1; length = input.read(buffer))
|
2018-09-22 17:33:00 +03:00
|
|
|
digest.update(buffer, 0, length);
|
2018-09-17 10:07:32 +03:00
|
|
|
return digest.digest();
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] digest(DigestAlgorithm algo, Path file) throws IOException {
|
|
|
|
try (InputStream input = IOHelper.newInput(file)) {
|
|
|
|
return digest(algo, input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] digest(DigestAlgorithm algo, String s) {
|
|
|
|
return digest(algo, IOHelper.encode(s));
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] digest(DigestAlgorithm algo, URL url) throws IOException {
|
|
|
|
try (InputStream input = IOHelper.newInput(url)) {
|
|
|
|
return digest(algo, input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static KeyPair genRSAKeyPair() {
|
|
|
|
return genRSAKeyPair(newRandom());
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static KeyPair genRSAKeyPair(SecureRandom random) {
|
|
|
|
try {
|
|
|
|
KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGO);
|
|
|
|
generator.initialize(RSA_KEY_LENGTH_BITS, random);
|
|
|
|
return generator.genKeyPair();
|
|
|
|
} catch (NoSuchAlgorithmException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static boolean isValidSign(byte[] bytes, byte[] sign, RSAPublicKey publicKey) throws SignatureException {
|
|
|
|
Signature signature = newRSAVerifySignature(publicKey);
|
|
|
|
try {
|
|
|
|
signature.update(bytes);
|
|
|
|
} catch (SignatureException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
return signature.verify(sign);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static boolean isValidSign(InputStream input, byte[] sign, RSAPublicKey publicKey) throws IOException, SignatureException {
|
|
|
|
Signature signature = newRSAVerifySignature(publicKey);
|
|
|
|
updateSignature(input, signature);
|
|
|
|
return signature.verify(sign);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static boolean isValidSign(Path path, byte[] sign, RSAPublicKey publicKey) throws IOException, SignatureException {
|
|
|
|
try (InputStream input = IOHelper.newInput(path)) {
|
|
|
|
return isValidSign(input, sign, publicKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static boolean isValidSign(URL url, byte[] sign, RSAPublicKey publicKey) throws IOException, SignatureException {
|
|
|
|
try (InputStream input = IOHelper.newInput(url)) {
|
|
|
|
return isValidSign(input, sign, publicKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static boolean isValidToken(CharSequence token) {
|
|
|
|
return token.length() == TOKEN_STRING_LENGTH && token.chars().allMatch(ch -> HEX.indexOf(ch) >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Cipher newCipher(String algo) {
|
|
|
|
// IDK Why, but collapsing catch blocks makes ProGuard generate invalid stackmap
|
|
|
|
try {
|
|
|
|
return Cipher.getInstance(algo);
|
|
|
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static MessageDigest newDigest(DigestAlgorithm algo) {
|
|
|
|
VerifyHelper.verify(algo, a -> a != DigestAlgorithm.PLAIN, "PLAIN digest");
|
|
|
|
try {
|
|
|
|
return MessageDigest.getInstance(algo.name);
|
|
|
|
} catch (NoSuchAlgorithmException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static SecureRandom newRandom() {
|
|
|
|
return new SecureRandom();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Cipher newRSACipher(int mode, RSAKey key) {
|
|
|
|
Cipher cipher = newCipher(RSA_CIPHER_ALGO);
|
|
|
|
try {
|
|
|
|
cipher.init(mode, (Key) key);
|
|
|
|
} catch (InvalidKeyException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
return cipher;
|
|
|
|
}
|
|
|
|
|
|
|
|
@LauncherAPI
|
|
|
|
public static Cipher newRSADecryptCipher(RSAPrivateKey key) {
|
|
|
|
return newRSACipher(Cipher.DECRYPT_MODE, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
@LauncherAPI
|
|
|
|
public static Cipher newRSAEncryptCipher(RSAPublicKey key) {
|
|
|
|
return newRSACipher(Cipher.ENCRYPT_MODE, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static KeyFactory newRSAKeyFactory() {
|
|
|
|
try {
|
|
|
|
return KeyFactory.getInstance(RSA_ALGO);
|
|
|
|
} catch (NoSuchAlgorithmException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Signature newRSASignature() {
|
|
|
|
try {
|
|
|
|
return Signature.getInstance(RSA_SIGN_ALGO);
|
|
|
|
} catch (NoSuchAlgorithmException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static Signature newRSASignSignature(RSAPrivateKey key) {
|
|
|
|
Signature signature = newRSASignature();
|
|
|
|
try {
|
|
|
|
signature.initSign(key);
|
|
|
|
} catch (InvalidKeyException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
return signature;
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static Signature newRSAVerifySignature(RSAPublicKey key) {
|
|
|
|
Signature signature = newRSASignature();
|
|
|
|
try {
|
|
|
|
signature.initVerify(key);
|
|
|
|
} catch (InvalidKeyException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
return signature;
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] randomBytes(int length) {
|
|
|
|
return randomBytes(newRandom(), length);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] randomBytes(Random random, int length) {
|
|
|
|
byte[] bytes = new byte[length];
|
|
|
|
random.nextBytes(bytes);
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
@LauncherAPI
|
|
|
|
public static String randomStringToken() {
|
|
|
|
return randomStringToken(newRandom());
|
|
|
|
}
|
|
|
|
|
|
|
|
@LauncherAPI
|
|
|
|
public static String randomStringToken(Random random) {
|
|
|
|
return toHex(randomToken(random));
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] randomToken() {
|
|
|
|
return randomToken(newRandom());
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] randomToken(Random random) {
|
|
|
|
return randomBytes(random, TOKEN_LENGTH);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-10-18 12:39:36 +03:00
|
|
|
public static String randomStringAESKey() {
|
|
|
|
return toHex(randomAESKey(newRandom()));
|
|
|
|
}
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-10-18 12:39:36 +03:00
|
|
|
public static String randomStringAESKey(Random random) {
|
|
|
|
return toHex(randomAESKey(random));
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-10-18 12:39:36 +03:00
|
|
|
public static byte[] randomAESKey() {
|
|
|
|
return randomAESKey(newRandom());
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-10-18 12:39:36 +03:00
|
|
|
public static byte[] randomAESKey(Random random) {
|
|
|
|
return randomBytes(random, AES_KEY_LENGTH);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static String randomUsername() {
|
|
|
|
return randomUsername(newRandom());
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static String randomUsername(Random random) {
|
|
|
|
int usernameLength = 3 + random.nextInt(7); // 3-9
|
|
|
|
|
|
|
|
// Choose prefix
|
|
|
|
String prefix;
|
|
|
|
int prefixType = random.nextInt(7);
|
|
|
|
if (usernameLength >= 5 && prefixType == 6) { // (6) 2-char
|
|
|
|
prefix = random.nextBoolean() ? "Mr" : "Dr";
|
|
|
|
usernameLength -= 2;
|
|
|
|
} else if (usernameLength >= 6 && prefixType == 5) { // (5) 3-char
|
|
|
|
prefix = "Mrs";
|
|
|
|
usernameLength -= 3;
|
|
|
|
} else
|
2018-09-22 17:33:00 +03:00
|
|
|
prefix = "";
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Choose suffix
|
|
|
|
String suffix;
|
|
|
|
int suffixType = random.nextInt(7); // 0-6, 7 values
|
|
|
|
if (usernameLength >= 5 && suffixType == 6) { // (6) 10-99
|
|
|
|
suffix = String.valueOf(10 + random.nextInt(90));
|
|
|
|
usernameLength -= 2;
|
|
|
|
} else if (usernameLength >= 7 && suffixType == 5) { // (5) 1990-2015
|
|
|
|
suffix = String.valueOf(1990 + random.nextInt(26));
|
|
|
|
usernameLength -= 4;
|
|
|
|
} else
|
2018-09-22 17:33:00 +03:00
|
|
|
suffix = "";
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Choose name
|
|
|
|
int consRepeat = 0;
|
|
|
|
boolean consPrev = random.nextBoolean();
|
|
|
|
char[] chars = new char[usernameLength];
|
|
|
|
for (int i = 0; i < chars.length; i++) {
|
|
|
|
if (i > 1 && consPrev && random.nextInt(10) == 0) { // Doubled
|
|
|
|
chars[i] = chars[i - 1];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Choose next char
|
|
|
|
if (consRepeat < 1 && random.nextInt() == 5)
|
2018-09-22 17:33:00 +03:00
|
|
|
consRepeat++;
|
|
|
|
else {
|
2018-09-17 10:07:32 +03:00
|
|
|
consRepeat = 0;
|
|
|
|
consPrev ^= true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Choose char
|
|
|
|
char[] alphabet = consPrev ? CONS : VOWELS;
|
|
|
|
chars[i] = alphabet[random.nextInt(alphabet.length)];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make first letter uppercase
|
|
|
|
if (!prefix.isEmpty() || random.nextBoolean())
|
2018-09-22 17:33:00 +03:00
|
|
|
chars[0] = Character.toUpperCase(chars[0]);
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Return chosen name (and verify for sure)
|
|
|
|
return VerifyHelper.verifyUsername(prefix + new String(chars) + suffix);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] sign(byte[] bytes, RSAPrivateKey privateKey) {
|
|
|
|
Signature signature = newRSASignSignature(privateKey);
|
|
|
|
try {
|
|
|
|
signature.update(bytes);
|
|
|
|
return signature.sign();
|
|
|
|
} catch (SignatureException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static byte[] sign(InputStream input, RSAPrivateKey privateKey) throws IOException {
|
|
|
|
Signature signature = newRSASignSignature(privateKey);
|
|
|
|
updateSignature(input, signature);
|
|
|
|
try {
|
|
|
|
return signature.sign();
|
|
|
|
} catch (SignatureException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static byte[] sign(Path path, RSAPrivateKey privateKey) throws IOException {
|
|
|
|
try (InputStream input = IOHelper.newInput(path)) {
|
|
|
|
return sign(input, privateKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static String toHex(byte[] bytes) {
|
|
|
|
int offset = 0;
|
|
|
|
char[] hex = new char[bytes.length << 1];
|
|
|
|
for (byte currentByte : bytes) {
|
|
|
|
int ub = Byte.toUnsignedInt(currentByte);
|
|
|
|
hex[offset] = HEX.charAt(ub >>> 4);
|
|
|
|
offset++;
|
|
|
|
hex[offset] = HEX.charAt(ub & 0x0F);
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
return new String(hex);
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static RSAPrivateKey toPrivateRSAKey(byte[] bytes) throws InvalidKeySpecException {
|
|
|
|
return (RSAPrivateKey) newRSAKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static RSAPublicKey toPublicRSAKey(byte[] bytes) throws InvalidKeySpecException {
|
|
|
|
return (RSAPublicKey) newRSAKeyFactory().generatePublic(new X509EncodedKeySpec(bytes));
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void updateSignature(InputStream input, Signature signature) throws IOException {
|
|
|
|
byte[] buffer = IOHelper.newBuffer();
|
|
|
|
for (int length = input.read(buffer); length >= 0; length = input.read(buffer))
|
2018-09-22 17:33:00 +03:00
|
|
|
try {
|
2018-09-17 10:07:32 +03:00
|
|
|
signature.update(buffer, 0, length);
|
|
|
|
} catch (SignatureException e) {
|
|
|
|
throw new InternalError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static void verifySign(byte[] bytes, byte[] sign, RSAPublicKey publicKey) throws SignatureException {
|
|
|
|
if (!isValidSign(bytes, sign, publicKey))
|
2018-09-22 17:33:00 +03:00
|
|
|
throw new SignatureException("Invalid sign");
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static void verifySign(InputStream input, byte[] sign, RSAPublicKey publicKey) throws SignatureException, IOException {
|
|
|
|
if (!isValidSign(input, sign, publicKey))
|
2018-09-22 17:33:00 +03:00
|
|
|
throw new SignatureException("Invalid stream sign");
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static void verifySign(Path path, byte[] sign, RSAPublicKey publicKey) throws SignatureException, IOException {
|
|
|
|
if (!isValidSign(path, sign, publicKey))
|
2018-09-22 17:33:00 +03:00
|
|
|
throw new SignatureException(String.format("Invalid file sign: '%s'", path));
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static void verifySign(URL url, byte[] sign, RSAPublicKey publicKey) throws SignatureException, IOException {
|
|
|
|
if (!isValidSign(url, sign, publicKey))
|
2018-09-22 17:33:00 +03:00
|
|
|
throw new SignatureException(String.format("Invalid URL sign: '%s'", url));
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
2018-10-25 11:43:42 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public static String verifyToken(String token) {
|
|
|
|
return VerifyHelper.verify(token, SecurityHelper::isValidToken, String.format("Invalid token: '%s'", token));
|
|
|
|
}
|
|
|
|
|
|
|
|
private SecurityHelper() {
|
|
|
|
}
|
2018-11-08 15:30:16 +03:00
|
|
|
|
2018-10-08 16:57:29 +03:00
|
|
|
//AES
|
|
|
|
public static byte[] encrypt(String seed, byte[] cleartext) throws Exception {
|
|
|
|
byte[] rawKey = getRawKey(seed.getBytes());
|
|
|
|
byte[] result = encrypt(rawKey, cleartext);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static byte[] encrypt(String seed, String cleartext) throws Exception {
|
|
|
|
return encrypt(seed, cleartext.getBytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
private static byte[] getRawKey(byte[] seed) throws Exception {
|
|
|
|
KeyGenerator kGen = KeyGenerator.getInstance("AES");
|
|
|
|
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
|
|
|
|
sr.setSeed(seed);
|
|
|
|
kGen.init(128, sr); // 192 and 256 bits may not be available
|
|
|
|
SecretKey sKey = kGen.generateKey();
|
|
|
|
return sKey.getEncoded();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
|
|
|
|
SecretKeySpec sKeySpec = new SecretKeySpec(raw, "AES");
|
|
|
|
Cipher cipher = Cipher.getInstance("AES");
|
|
|
|
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
|
|
|
|
return cipher.doFinal(clear);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
|
|
|
|
SecretKeySpec sKeySpec = new SecretKeySpec(raw, "AES");
|
|
|
|
Cipher cipher = Cipher.getInstance("AES");
|
|
|
|
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
|
|
|
|
return cipher.doFinal(encrypted);
|
|
|
|
}
|
2018-11-08 15:30:16 +03:00
|
|
|
|
2018-10-08 16:57:29 +03:00
|
|
|
public static byte[] HexToByte(String hexString) {
|
|
|
|
int len = hexString.length() / 2;
|
|
|
|
byte[] result = new byte[len];
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|