шифр подстановки с другой длиной алфавита

Я хотел бы реализовать простой шифр подстановки для маскирования частных идентификаторов в URL.

Я знаю, как мои идентификаторы будут похожи (комбинация прописных букв ASCII, цифр и подчеркивания), и они будут довольно длинны, поскольку они - составленные ключи. Я хотел бы использовать более длинный алфавит для сокращения получающихся кодов (я хотел бы использовать верхний - и строчные буквы ASCII, цифры и ничто иное). Таким образом, мой входящий алфавит был бы

[A-Z0-9_] (37 chars)

и мой исходящий алфавит был бы

[A-Za-z0-9] (62 chars)

таким образом, сжатие почти 50%-го разумного объема сжатия было бы доступно.

Скажем, мои URL похожи на это:

/my/page/GFZHFFFZFZTFZTF_24_F34

и я хочу, чтобы они были похожи на это вместо этого:

/my/page/Ft32zfegZFV5

Очевидно, оба массива были бы переставлены для введения некоторого произвольного порядка.

Это не должно быть безопасно. Если кто-то понимает это: прекрасный, но я не хочу план быть очевидным.

Мое требуемое решение состояло бы в том, чтобы преобразовать строку в целочисленное представление основания 37, преобразовать основание в 62 и использовать второй алфавит для выписывания того числа. действительно ли там какой-либо пример кода доступен, который делает что-то подобное? Integer.parseInt() имеет некоторую подобную логику, но она трудно кодируется для использования стандартного поведения цифры.

Какие-либо идеи?

Я использую Java для реализации этого, но код или псевдокод на любом другом языке, конечно, также полезны.

7
задан Sean Patrick Floyd 27 November 2012 в 16:26
поделиться

3 ответа

Необъяснимо Character.MAX_RADIX всего 36, но вы всегда можете написать свою собственную базовую процедуру преобразования. Следующая реализация не является высокопроизводительной, но она должна быть хорошей отправной точкой:

import java.math.BigInteger;
public class BaseConvert {
    static BigInteger fromString(String s, int base, String symbols) {
        BigInteger num = BigInteger.ZERO;
        BigInteger biBase = BigInteger.valueOf(base);
        for (char ch : s.toCharArray()) {
            num = num.multiply(biBase)
                     .add(BigInteger.valueOf(symbols.indexOf(ch)));
        }
        return num;
    }
    static String toString(BigInteger num, int base, String symbols) {
        StringBuilder sb = new StringBuilder();
        BigInteger biBase = BigInteger.valueOf(base);
        while (!num.equals(BigInteger.ZERO)) {
            sb.append(symbols.charAt(num.mod(biBase).intValue()));
            num = num.divide(biBase);
        }
        return sb.reverse().toString();
    }
    static String span(char from, char to) {
        StringBuilder sb = new StringBuilder();
        for (char ch = from; ch <= to; ch++) {
            sb.append(ch);
        }
        return sb.toString();
    }
}

Тогда вы можете использовать тестовую систему main () , подобную следующей:

public static void main(String[] args) {
    final String SYMBOLS_AZ09_ = span('A','Z') + span('0','9') + "_";
    final String SYMBOLS_09AZ = span('0','9') + span('A','Z');
    final String SYMBOLS_AZaz09 = span('A','Z') + span('a','z') + span('0','9');

    BigInteger n = fromString("GFZHFFFZFZTFZTF_24_F34", 37, SYMBOLS_AZ09_);

    // let's convert back to base 37 first...
    System.out.println(toString(n, 37, SYMBOLS_AZ09_));
    // prints "GFZHFFFZFZTFZTF_24_F34"

    // now let's see what it looks like in base 62...       
    System.out.println(toString(n, 62, SYMBOLS_AZaz09));
    // prints "ctJvrR5kII1vdHKvjA4"

    // now let's test with something we're more familiar with...
    System.out.println(fromString("CAFEBABE", 16, SYMBOLS_09AZ));
    // prints "3405691582"

    n = BigInteger.valueOf(3405691582L);
    System.out.println(toString(n, 16, SYMBOLS_09AZ));
    // prints "CAFEBABE"        
}

Некоторые наблюдения

  • BigInteger , вероятно, проще всего, если числа могут превышать long
  • . Вы можете перемешать char в символе String , просто придерживайтесь одной «секретной» перестановки

Примечание относительно «50% сжатия»

Обычно нельзя ожидать, что строка с основанием 62 будет примерно вдвое короче, чем строка с основанием 36. Вот Long.MAX_VALUE в базе 10, 20 и 30:

    System.out.format("%s%n%s%n%s%n",
        Long.toString(Long.MAX_VALUE, 10), // "9223372036854775807"
        Long.toString(Long.MAX_VALUE, 20), // "5cbfjia3fh26ja7"
        Long.toString(Long.MAX_VALUE, 30)  // "hajppbc1fc207"
    );
3
ответ дан 7 December 2019 в 12:16
поделиться

Это вообще не подстановочный шифр, но ваш вопрос достаточно ясен.

Взгляните на Base85: http://en.wikipedia.org/wiki/Ascii85

Для Java (как косвенно указано в статье Википедии):

2
ответ дан 7 December 2019 в 12:16
поделиться

Теперь у меня есть рабочее решение, которое вы можете найти здесь:

http://pastebin.com/Mctnidng

Проблема заключалась в том, что а) я терял точность в длинных кодах через эту часть:

value = value.add(//
    BigInteger.valueOf((long) Math.pow(alphabet.length, i)) // error here
        .multiply(
            BigInteger.valueOf(ArrayUtils.indexOf(alphabet, c))));

(длинного просто недостаточно)

и б) всякий раз, когда у меня был текст, начинающийся с символа со смещением 0 в алфавите, он отбрасывался, поэтому мне нужно было добавить длина символа (здесь подойдет один символ, поскольку мои коды никогда не будут такими же длинными, как алфавит)

0
ответ дан 7 December 2019 в 12:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: