具有不同字母长度的替代密码 [英] substitution cipher with different alphabet length
问题描述
我想实现一个简单的替换密码来掩盖URL中的私有ID。
我知道我的ID如何(大写ASCII字母,数字的组合和下划线),它们将是相当长的,因为它们是组合键。我想使用较长的字母表来缩短生成的代码(我想使用大小写ASCII字母,数字等等)。所以我输入的字母将是
[A-Z0-9_](37个字符)
,我的传出字母将为
[A-Za-z0-9](62个字符)
所以压缩几乎50%可以获得合理的压缩数量。
我们的网址如下所示:
/ my / page / GFZHFFFZFZTFZTF_24_F34
我想要他们看起来像这样:
/ my / page / Ft32zfegZFV5
显然,这两个数组都会被洗牌,以便带来一些随机顺序。
这不一定要安全。如果有人说出来:好的,但是我不希望这个方案是显而易见的。
我想要的解决方案是将字符串转换为基数的整数表示37,将小数转换为62,并使用第二个字母表写出该数字。有没有可用的示例代码做类似的事情? Integer.parseInt()
有一些类似的逻辑,但是使用标准数字行为是硬编码的。
任何想法?
我正在使用Java来实现这一点,但是使用任何其他语言的代码或伪代码当然也是有帮助的。
不可思议的是, 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_);
//先转换回基数37
System.out.println(toString(n,37,SYMBOLS_AZ09_));
//打印GFZHFFFZFZTFZTF_24_F34
//现在让我们看看它在底层62中的外观...
System.out.println(toString(n,62,SYMBOLS_AZaz09 ));
//打印ctJvrR5kII1vdHKvjA4
//现在让我们用更熟悉的东西进行测试...
System.out.println(fromString(CAFEBABE, 16,SYMBOLS_09AZ));
//打印3405691582
n = BigInteger.valueOf(3405691582L);
System.out.println(toString(n,16,SYMBOLS_09AZ));
//打印CAFEBABE
}
一些观察
-
BigInteger
如果数字可以超过long
- 您可以在符号
String
中的char
只要坚持一个秘密排列
关于50%压缩的注意事项
你不能一般期望base 62字符串的长度大约是基础36字符串的一半。这是$ 10 $,$ 20 $和$ 30 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ System.out.format(%s%n%s%n%s%n,
Long.toString(Long.MAX_VALUE,10),//9223372036854775807
Long.toString $ MAX_VALUE,20),//5cbfjia3fh26ja7
Long.toString(Long.MAX_VALUE,30)//hajppbc1fc207
);
I would like to implement a simple substitution cipher to mask private ids in URLs.
I know how my IDs will look like (combination of uppercase ASCII letters, digits and underscore), and they will be rather long, as they are composed keys. I would like to use a longer alphabet to shorten the resulting codes (I'd like to use upper- and lowercase ASCII letters, digits and nothing else). So my incoming alphabet would be
[A-Z0-9_] (37 chars)
and my outgoing alphabet would be
[A-Za-z0-9] (62 chars)
so a compression of almost 50% reasonable amount of compression would be available.
Let's say my URLs look like this:
/my/page/GFZHFFFZFZTFZTF_24_F34
and I want them to look like this instead:
/my/page/Ft32zfegZFV5
Obviously both arrays would be shuffled to bring some random order in.
This does not have to be secure. If someone figures it out: fine, but I don't want the scheme to be obvious.
My desired solution would be to convert the string to an integer representation of radix 37, convert the radix to 62 and use the second alphabet to write out that number. is there any sample code available that does something similar? Integer.parseInt()
has some similar logic, but it is hard-coded to use standard digit behavior.
Any ideas?
I am using Java to implement this but code or pseudo-code in any other language is of course also helpful.
Inexplicably Character.MAX_RADIX
is only 36, but you can always write your own base conversion routine. The following implementation isn't high-performance, but it should be a good starting point:
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();
}
}
Then you can have a main()
test harness like the following:
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"
}
Some observations
BigInteger
is probably easiest if the numbers can exceedlong
- You can shuffle the
char
in the symbolString
, just stick to one "secret" permutation
Note regarding "50% compression"
You can't generally expect the base 62 string to be around half as short as the base 36 string. Here's Long.MAX_VALUE
in base 10, 20, and 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"
);
这篇关于具有不同字母长度的替代密码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!