【问题标题】:Separate database for random strings?随机字符串的单独数据库?
【发布时间】:2012-10-16 23:22:11
【问题描述】:

当用户在我的网站上创建订单时,该订单需要一个在应用程序中应该是唯一的代码。我不想使用 GUID,因为它们又长又笨拙——我只想要一个 8 个字符或其他字符的字母数字代码。

我认为最好预先生成这些代码,这样在运行时代码就不会因为寻找独特的东西而陷入困境。

此外,我正在考虑将这些代码放入一个单独的数据库中,这样备份和移动我的主数据库就不会涉及对数兆字节的随机字符串进行改组 - 使用的值将被复制到主数据库。

这听起来是个好主意吗?

【问题讨论】:

    标签: string random uniqueidentifier


    【解决方案1】:

    不需要数据库。加密将为您提供一组随机的唯一数字。 DES 使用 64 位块,因此通过加密 0、1、2、3...,您将得到一组随机出现的 64 位数字。

    没有 32 位块大小的标准密码,但您可以为自己编写一个简单的 Feistel cypher(见下文)或使用具有可变块大小的 Hasty Pudding 密码。

    /**
     * IntegerPerm is a reversible keyed permutation of the integers.
     * This class is not cryptographically secure as the F function
     * is too simple and there are not enough rounds.
     *
     * @author rossum
     */
    public final class IntegerPerm {
        //////////////////
        // Private Data //
        //////////////////
    
        /** Non-zero default key, from www.random.org */
        private final static int DEFAULT_KEY = 0x6CFB18E2;
    
        private final static int LOW_16_MASK = 0xFFFF;
        private final static int HALF_SHIFT = 16;
        private final static int NUM_ROUNDS = 4;
    
        /** Permutation key */
        private int mKey;
    
        /** Round key schedule */
        private int[] mRoundKeys = new int[NUM_ROUNDS];
    
        //////////////////
        // Constructors //
        //////////////////
        public IntegerPerm() { this(DEFAULT_KEY); }
    
        public IntegerPerm(int key) { setKey(key); }
    
    
        ////////////////////
        // Public Methods //
        ////////////////////
        /** Sets a new value for the key and key schedule. */
        public void setKey(int newKey) {
            assert (NUM_ROUNDS == 4) : "NUM_ROUNDS is not 4";
            mKey = newKey;
    
            mRoundKeys[0] = mKey & LOW_16_MASK;
            mRoundKeys[1] = ~(mKey & LOW_16_MASK);
            mRoundKeys[2] = mKey >>> HALF_SHIFT;
            mRoundKeys[3] = ~(mKey >>> HALF_SHIFT);
        } // end setKey()
    
        /** Returns the current value of the key. */
        public int getKey() { return mKey; }
    
        /**
         * Calculates the enciphered (i.e. permuted) value of the given integer
         * under the current key.
         *
         * @param plain the integer to encipher.
         *
         * @return the enciphered (permuted) value.
         */
        public int encipher(int plain) {
            // 1 Split into two halves.
            int rhs = plain & LOW_16_MASK;
            int lhs = plain >>> HALF_SHIFT;
    
            // 2 Do NUM_ROUNDS simple Feistel rounds.
            for (int i = 0; i < NUM_ROUNDS; ++i) {
                if (i > 0) {
                    // Swap lhs <-> rhs
                    final int temp = lhs;
                    lhs = rhs;
                    rhs = temp;
                } // end if
                // Apply Feistel round function F().
                rhs ^= F(lhs, i);
            } // end for
    
            // 3 Recombine the two halves and return.
            return (lhs << HALF_SHIFT) + (rhs & LOW_16_MASK);
        } // end encipher()
    
    
        /**
         * Calculates the deciphered (i.e. inverse permuted) value of the given
         * integer under the current key.
         *
         * @param cypher the integer to decipher.
         *
         * @return the deciphered (inverse permuted) value.
         */
        public int decipher(int cypher) {
            // 1 Split into two halves.
            int rhs = cypher & LOW_16_MASK;
            int lhs = cypher >>> HALF_SHIFT;
    
            // 2 Do NUM_ROUNDS simple Feistel rounds.
            for (int i = 0; i < NUM_ROUNDS; ++i) {
                if (i > 0) {
                    // Swap lhs <-> rhs
                    final int temp = lhs;
                    lhs = rhs;
                    rhs = temp;
                } // end if
                // Apply Feistel round function F().
                rhs ^= F(lhs, NUM_ROUNDS - 1 - i);
            } // end for
    
            // 4 Recombine the two halves and return.
            return (lhs << HALF_SHIFT) + (rhs & LOW_16_MASK);
        } // end decipher()
    
    
        /////////////////////
        // Private Methods //
        /////////////////////
    
        // The F function for the Feistel rounds.
        private int F(int num, int round) {
            // XOR with round key.
            num ^= mRoundKeys[round];
            // Square, then XOR the high and low parts.
            num *= num;
            return (num >>> HALF_SHIFT) ^ (num & LOW_16_MASK);
        } // end F()
    
    } // end class IntegerPerm
    

    【讨论】:

    • 这不会导致重复随机数吗?
    • 没有。因为它是一种加密,所以它是可逆的,因此没有重复。如果您使用相同的键,不同的输入将总是导致不同的输出。块密码(就是这样)是块的所有可能值的排列。域和范围的大小相同,因此没有重复,也没有遗漏任何值。坚持使用单个密钥值,并按顺序加密 0、1、2、3、...,不重复,并且保证输出永远不会重复,直到您溢出回 0。
    • 听起来不错。一个问题是我已经在我的数据库中预先生成了几年的随机订单代码。所以我认为我需要更改格式,否则这种机制可能会产生重复。如果密钥和代码也被泄露,也会出现同样的问题。 (部分想法是阻止用户计算已下订单的数量。)
    • 密钥确实必须保密,而不是密码。任何攻击者总是被假定知道代码。请参阅en.wikipedia.org/wiki/Kerckhoffs's_principle 是的,您需要分离出旧的订单代码。也许只是在旧代码或新代码中添加一个区别字符,X##### 是旧代码,而 K##### 是新代码或其他。
    • 我认为这是该机制的一个弱点——如果不是一个非常严重的弱点的话。没有办法知道是否有人获得了密钥的访问权限。通常,人们可能会定期更改密钥,但在这种情况下,这意味着更改生成代码的格式,并且大约 8 个字母数字字符的格式并不多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-11
    • 2017-05-20
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多