代码之家  ›  专栏  ›  技术社区  ›  Sean Patrick Floyd

不同字母长度的替换密码

  •  7
  • Sean Patrick Floyd  · 技术社区  · 14 年前

    我想实现一个简单的替换密码来屏蔽URL中的私有ID。

    我知道我的ID将是什么样子(大写的ASCII字母、数字和下划线的组合),而且它们将相当长,因为它们是组合键。我想用一个较长的字母表来缩短结果代码(我想用大小写的ASCII字母、数字等)。所以我输入的字母表应该是

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

    我的外发字母表是

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

    所以A 压缩率接近50% 可提供合理的压缩量。

    假设我的URL如下所示:

    /my/page/GFZHFFFZFZTFZTF_24_F34
    

    我希望他们看起来像这样:

    /my/page/Ft32zfegZFV5
    

    很明显,这两个数组都会进行无序排列,以获得一些随机顺序。

    这不一定是安全的。如果有人发现了:好的,但我不想让计划变得明显。

    我想要的解决方案是将字符串转换为基数37的整数表示,将基数转换为62,然后使用第二个字母表写出该数字。是否有类似的示例代码可用? Integer.parseInt() 有一些类似的逻辑,但很难用标准的数字行为进行编码。

    有什么想法吗?

    我使用Java来实现这一点,但是代码或伪代码在任何其他语言中当然也是有帮助的。

    3 回复  |  直到 12 年前
        1
  •  3
  •   polygenelubricants    14 年前

    莫名其妙地 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
  •  0
  •   Sean Patrick Floyd    14 年前

    我现在有一个有效的解决方案,您可以在这里找到:

    http://pastebin.com/Mctnidng

    问题是a)我在这部分中丢失了长代码的精度:

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

    (时间不够长)

    b)每当我有一个以字母表中偏移量0处的字符开头的文本时,这个字符就会被删除,因此我需要添加一个长度字符(这里一个字符做得很好,因为我的代码永远不会和字母表一样长)