16

我有一个字符串如下:

  • 它的长度是 10。
  • 它代表基数 36,因此包括数字和大写字母。
  • 字符串的来源是由数据库生成的序列(即从 1 及以上),正在转换为 base 36。

我的问题是转换为base 36转换的结果也是连续/连续的;例如:

    ID: 1402 -> 000000012Y    
    ID: 1403 -> 000000012Z    
    ID: 1404 -> 0000000130   
    ID: 1404 -> 0000000131  
    ID: 1404 -> 0000000132

我正在寻找一个简短的简单算法,可以混合基数 36 结果,其中:

  • 我只能使用允许的 36 个基数字符(数字和大写字母)。
  • 该算法仅用于混淆/混合 base 36 字符串;我不需要加密等。
  • 这里的主要问题是结果不会是连续的。
  • 我需要能够对混淆结果进行去混淆/分解。

我尝试了一些转换字符逻辑,但我陷入了连续结果问题。
我想我需要在这里添加一些数学方面。

我将不胜感激任何尽可能简单的想法,如果可能的话,请使用代码示例。

4

5 回答 5

26

如果只有一个随机顺序包含 36 个字符的数组呢?类似于 One-time pad 加密但有一个固定的 pad:

static String source="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static String target="Q5A8ZWS0XEDC6RFVT9GBY4HNU3J2MI1KO7LP";

public static String obfuscate(String s) {
    char[] result= new char[10];
    for (int i=0;i<s.length();i++) {
        char c=s.charAt(i);
        int index=source.indexOf(c);
        result[i]=target.charAt(index);
    }

    return new String(result);
}

public static String unobfuscate(String s) {
    char[] result= new char[10];
    for (int i=0;i<s.length();i++) {
        char c=s.charAt(i);
        int index=target.indexOf(c);
        result[i]=source.charAt(index);
    }

    return new String(result);
}

所以一个 10 个字符的字符串"HELLO12345"就变成了"0ZCCF2MI1K"。混淆,但未加密

于 2013-05-14T12:43:23.073 回答
24

你在寻找这样的东西吗?

import java.util.Locale;

public class Obfuscate {

    //adjust to suit:
    final static int feistelRounds = 4;
    final static int randRounds = 4;
    final static int seed = 12345;

    // modulus for half a string:
    final static int mod = 60466176; //36^5

    private static int f (int x) {
        // http://en.wikipedia.org/wiki/Linear_congruential_generator
        final int a = 12+1;
        final int c = 1361423303;
        x = (x + seed) % mod;
        int r = randRounds;
        while (r-- != 0) {
            x = (a*x+c) % mod;
        }
        return x;
    }

    public static String obfuscate (int i) {
        int a = i / mod;
        int b = i % mod;
        int r = feistelRounds;
        while (r-- != 0) {
            a = (a + f(b)) % mod;
            b = (b + f(a)) % mod;
        }
        return pad5(Integer.toString(a, 36)) + pad5(Integer.toString(b, 36));
    }

    public static int illuminate (String s) {
        int a = Integer.valueOf(s.substring(0,5),36);
        int b = Integer.valueOf(s.substring(5,10),36);
        int r = feistelRounds;
        while (r-- != 0) {
            b = (b - f(a)) % mod;
            a = (a - f(b)) % mod;
        }
        // make the modulus positive:
        a = (a + mod)%mod;
        b = (b + mod)%mod;

        return a*mod+b;
    }

    public static String pad5(String s) {
        return String.format("%5s", s).replace(' ', '0').toUpperCase(Locale.ENGLISH);
    }

    public static String pad10(String s) {
        return String.format("%10s", s).replace(' ', '0').toUpperCase(Locale.ENGLISH);
    }

    // demonstration
    public static void main(String[] args) {
        for (int i = 0; i<20; i++) {
            System.out.printf("%08d -> %s -> %08d\n", i, obfuscate(i), illuminate(obfuscate(i)));
        }
    }
}

输出:

00000000 -> P2TH9ZW2VI -> 00000000
00000001 -> G47GI9ZR9S -> 00000001
00000002 -> 75LFRK3FO2 -> 00000002
00000003 -> Y6ZF0U742C -> 00000003
00000004 -> P8DE94ASGM -> 00000004
00000005 -> G9RDIEEGUW -> 00000005
00000006 -> 7B5CROI596 -> 00000006
00000007 -> YCJC0YLTNG -> 00000007
00000008 -> PDXB98PI1Q -> 00000008
00000009 -> GFBAIIT6G0 -> 00000009
00000010 -> 7GP9RSWUUA -> 00000010
00000011 -> YI39030J8K -> 00000011
00000012 -> PJH89D47MU -> 00000012
00000013 -> GKV7IN7W14 -> 00000013
00000014 -> 7M96RXBKFE -> 00000014
00000015 -> YNN607F8TO -> 00000015
00000016 -> PP159HIX7Y -> 00000016
00000017 -> GQF4IRMLM8 -> 00000017
00000018 -> 7RT3R1QA0I -> 00000018
00000019 -> YT730BTYES -> 00000019

基本上,这是一个玩具,完全不安全,虽然写起来很有趣,加密算法。(加密确实是您所要求的——其他人无法理解但您可以逆转的输出。)我使用简单的 prng作为f 函数。

不过,结果很漂亮,对吧?如上所述,DES 会更安全。但是,如果你宁愿重新发明轮子(我自己也有点抗拒这种冲动)并且真正的安全不是问题,那么这是一个合理的起点。顺便说一句,DES 也基于 Feistel 网络。

实际上,根据您的要求,可能存在基于非加密的解决方案。如果这是,比如说,一个需要检查但没有猜到的优惠券代码,我只需在我的数据库中创建一个表,将 id 与随机生成的 10 个字符代码相关联(或将代码列添加到现有表优惠券)并在它们进入时进行查找。这当然需要编码和恢复软件能够访问相同的数据库,或者能够进行通信。

于 2013-05-08T22:33:17.077 回答
8

这是一个通用的解决方案,这是一个非常快速的算法,可以处理任何编码的任何字符串。

源代码

public class Translator {

    private static final String key = "Zx" + Math.log(2) / 3;

    public static String obfuscate(String s) {
        char[] result = new char[s.length()];
        for (int i = 0; i < s.length(); i++) {
            result[i] = (char) (s.charAt(i) + key.charAt(i % key.length()));
        }

        return new String(result);
    }

    public static String unobfuscate(String s) {
        char[] result = new char[s.length()];
        for (int i = 0; i < s.length(); i++) {
            result[i] = (char) (s.charAt(i) - key.charAt(i % key.length()));
        }

        return new String(result);
    }
}

用法

String obfuscate = Translator.obfuscate("Hi there");
System.out.println(obfuscate + " - " + Translator.unobfuscate(obfuscate));

输出:

¢áP¢£ - Hi there
于 2016-10-15T11:01:28.020 回答
4

在进行 base36 编码之前,只需反转计数器上的位即可。像这样的东西

public static void main(String[] args) {
    for (int i = 1400; i < 1420; i++) {
        String base36 = Integer.toString(i, 36);
        String reverse = Integer.toString(Integer.reverse(i << 1), 36);

        System.out.println("i: " + i + "  base36: " + base36 + 
                                       "  reverse: " + reverse);
    }
}

结果:

i: 1400  base36: 12w  reverse: 48ya68
i: 1401  base36: 12x  reverse: m08ao0
i: 1402  base36: 12y  reverse: d4laf4
i: 1403  base36: 12z  reverse: uvvaww
i: 1404  base36: 130  reverse: 8orsao
i: 1405  base36: 131  reverse: qg1ssg
i: 1406  base36: 132  reverse: hkesjk
i: 1407  base36: 133  reverse: zbot1c
i: 1408  base36: 134  reverse: 8464g
i: 1409  base36: 135  reverse: hze6m8
i: 1410  base36: 136  reverse: 93r6dc
i: 1411  base36: 137  reverse: qv16v4
i: 1412  base36: 138  reverse: 4nxo8w
i: 1413  base36: 139  reverse: mf7oqo
i: 1414  base36: 13a  reverse: djkohs
i: 1415  base36: 13b  reverse: vauozk
i: 1416  base36: 13c  reverse: 2g0x6o
i: 1417  base36: 13d  reverse: k7axog
i: 1418  base36: 13e  reverse: bbnxfk
i: 1419  base36: 13f  reverse: t2xxxc
于 2015-12-19T12:23:59.843 回答
2

除非这是家庭作业,否则我建议您使用 Base64 encoding: new sun.misc.BASE64Encoder().encode(string.getBytes())

这不会加密字符串,但会使其不可读。

如果您真的想加密字符串,请使用 java cryptography API,例如:

        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.ENCRYPT_MODE, password);
        String encrypedStr = base64encoder.encode(cipher.doFinal(cleartext));

现在encryptedString已加密并以 base64 格式存储。

您可以轻松找到如何解密字符串。祝你好运。

于 2013-05-05T12:24:55.660 回答