1

これは、この質問のフォローアップです。

受け入れられた答えは一般的に十分ですが、キーを生成するためにユーザーが個人情報(名前など)を提供する必要があります。

プログラムが特定の製品に属するかどうかを検証できるように、共通のシードに基づいて異なるキーを生成できるかどうか疑問に思っていますが、このプロセスをエンドユーザーに明確にする必要はありません。

つまり、製品IDのハッシュとランダムな文字シーケンスの組み合わせである可能性がありますが、これにより、ユーザーは潜在的な新しいキーを推測できます。推測するのが難しいある種のアルゴリズムがあるはずです。

4

2 に答える 2

6

標準の対称/非対称/ハッシュアルゴリズム(MDx、SHAx、RSAなど)の問題は、20/30文字の長さのプロダクトキー文字列のような非常に短い文字列では動作できないことです。ユーザーに最大1000文字の長さの文字列を提供できる場合は、これらのアルゴリズムを使用します。

できない場合は、「ほぼ完全にランダムな」短いプロダクトキーを作成できるが、バイトが隠されているC#コードを次に示します。その「隠しバイト」には好きなものを入れることができ、キーは推測しにくいアルゴリズムに従います。次のように、TryReadKey()を使用して、渡された文字列を検証し、バイトを再読み取りできます。

string key = BuildKey(myHiddenByte); // give that to the end user
...
byte hidden;
if (!TryReadKey(key, out hidden))
{
   Console.WriteLine("key is not ok");
}
else
{
   Console.WriteLine("key is ok and hidden byte is " + hidden);
}

アルゴリズムはステガノグラフィの原理を使用しており、確かに防弾ではなく、改善される可能性がありますが、役立つ可能性があります。キーの生成には時間がかかる場合がありますが、これは正常です。次のようなキーが生成されます(最初の単語は6文字の長さで、他の5文字の長さであることに注意してください)。

KZGMB0-XYJC2-YRKH3-8LD8G-5FUZG
YKU93K-M34PD-E5PL0-QM91J-QLDZF
DH27H9-NCW8E-EMGPL-YCJXJ-N2PRG
WDAKDE-G56NR-6BA3R-0JE6U-625EB
6D5EJ0-NJDAK-EMGZR-Z6ZDF-JHJGF
....

コードは次のとおりです。

    // need 32 characters, so we can use a 5-bit base encoding
    //                               0         1         2         3
    //                               01234567890123456789012345678901
    private const string KeyChars = "ABCDEFGHJKLMNPQRTUWXYZ0123456789";
    private const int MinSum = 2000;
    private const int Mask = 0xFFFFF;  // beware, this can have a dramatic influence on performance

    /// <summary>
    /// Builds a key and hides data in it.
    /// Key format is XXXXXX-XXXXX-XXXXX-XXXXX-XXXXX.
    /// </summary>
    /// <param name="hiddenData">The hidden data.</param>
    /// <returns>The build key</returns>
    public static string BuildKey(byte hiddenData)
    {
        int index;
        Guid guid;
        uint[] word = new uint[4];
        byte[] array;
        while (true)
        {
            // we use guid initial randomness characteristics
            guid = Guid.NewGuid();
            array = guid.ToByteArray();

            int sum = array.Aggregate(0, (current, b) => current + b);

            // a simple check to make sure the guid is not filled with too much zeros...
            if (sum < MinSum)
                continue;

            // build a 32-bit word array
            for (int i = 0; i < 4; i++)
            {
                word[i] = BitConverter.ToUInt32(array, i * 4) & Mask;
            }

            // Here we check the guid follows some algorithm. if it doesn't we skip it
            // the algorithm presented here is to check one of the 32-bit word is equal to the sum of the others
            // (modulo a mask otherwise it takes too long!)
            //
            // This algorithm can be changed at your will (and change the TryReadKey as well)

            if ((word[0] + word[1] + word[2]) == word[3])
            {
                index = 3;
                break;
            }

            if ((word[0] + word[1] + word[3]) == word[2])
            {
                index = 2;
                break;
            }

            if ((word[0] + word[3] + word[2]) == word[1])
            {
                index = 1;
                break;
            }

            if ((word[3] + word[1] + word[2]) == word[0])
            {
                index = 0;
                break;
            }
        }

        // hidden info is also xor'd with other words hi order (except the one we masked)
        // so it's not too easy to guess
        hiddenData = (byte)(hiddenData ^ array[0] ^ array[4] ^ array[8] ^ array[12]);

        // rebuild words without mask
        for (int i = 0; i < 4; i++)
        {
            word[i] = BitConverter.ToUInt32(array, i * 4);
        }

        // hidden info is stored in the block that is the sum of other blocks, in hi order byte (outside the mask)
        word[index] &= 0x00FFFFFF;
        word[index] |= ((uint)hiddenData) << 24;

        // rebuild the array back
        for (int i = 0; i < 4; i++)
        {
            byte[] ui = BitConverter.GetBytes(word[i]);
            for (int j = 0; j < 4; j++)
            {
                array[i * 4 + j] = ui[j];
            }
        }

        // now use 5-bits encoding
        int encodingBitIndex = 0;
        StringBuilder key = new StringBuilder();
        while (encodingBitIndex < 128)
        {
            byte encodingByte = Get5Bits(array, encodingBitIndex);
            if ((key.Length > 0) && (key.Length % 6) == 0)
            {
                key.Append('-');
            }
            key.Append(KeyChars[encodingByte]);
            encodingBitIndex += 5;
        }
        return key.ToString();
    }

    /// <summary>
    /// Validates the specified key and reads hidden data from it.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <param name="hiddenData">The hidden data.</param>
    /// <returns>true if the key is valid and hidden data was read; false otherwise.</returns>
    public static bool TryReadKey(string key, out byte hiddenData)
    {
        hiddenData = 0;
        if (key == null)
            return false;

        key = key.Replace("-", string.Empty);
        if (key.Length != 26)
            return false;

        byte[] array = new byte[16];
        int encodingBitIndex = 0;
        foreach (char t in key)
        {
            byte b = 255;
            for (byte k = 0; k < KeyChars.Length; k++)
            {
                if (KeyChars[k] != t) continue;
                b = k;
                break;
            }
            if (b == 255) // char not found
                return false;

            Put5Bits(array, encodingBitIndex, b);
            encodingBitIndex += 5;
        }

        // quick sum check
        int sum = array.Aggregate(0, (current, b) => current + b);

        // add 256 because we changed the hidden byte
        sum += 256;
        if (sum < MinSum)
            return false;

        uint[] word = new uint[4];
        for (int i = 0; i < 4; i++)
        {
            word[i] = BitConverter.ToUInt32(array, i * 4) & Mask;
        }

        // This must match BuildKey algorithm

        int index;
        if ((word[0] + word[1] + word[2]) == word[3])
        {
            index = 3;
        }
        else if ((word[0] + word[1] + word[3]) == word[2])
        {
            index = 2;
        }
        else if ((word[0] + word[3] + word[2]) == word[1])
        {
            index = 1;
        }
        else if ((word[3] + word[1] + word[2]) == word[0])
        {
            index = 0;
        }
        else
            return false;

        // reread word & extract hidden byte back
        word[index] = BitConverter.ToUInt32(array, index * 4);
        hiddenData = (byte)(word[index] >> 24);
        hiddenData = (byte)(hiddenData ^ array[0] ^ array[4] ^ array[8] ^ array[12]);
        return true;
    }

    // get 5 bits from a byte buffer at an arbitrary bit index
    private static byte Get5Bits(byte[] buffer, int bitIndex)
    {
        int r = bitIndex % 8;
        if (r < 4)
            return (byte)(((buffer[bitIndex / 8]) & (0xFF >> r)) >> (3 - r));

        byte b0 = (byte)((buffer[bitIndex / 8] & (0xFF >> r)) << (r - 3));
        if ((1 + (bitIndex / 8)) == buffer.Length) // last
            return (byte)(buffer[buffer.Length - 1] & 0x7);

        if ((bitIndex / 8) < 16)
            return (byte)(b0 | buffer[1 + (bitIndex / 8)] >> (11 - r));

        return b0;
    }

    // put 5 bits into a byte buffer at an arbitrary bit index
    private static void Put5Bits(byte[] buffer, int bitIndex, byte value)
    {
        int r = bitIndex % 8;
        if (r < 4)
        {
            buffer[bitIndex / 8] |= (byte)((value << (3 - r)));
        }
        else
        {
            if ((1 + (bitIndex / 8)) == buffer.Length) // last
            {
                buffer[buffer.Length - 1] |= (byte)(value & 0x7);
            }
            else if ((bitIndex / 8) < 16)
            {
                buffer[bitIndex / 8] |= (byte)((value >> (r - 3)));
                buffer[1 + (bitIndex / 8)] |= (byte)((value << (11 - r)));
            }
        }
    }
于 2011-01-05T10:41:53.670 に答える
0

キーによって生成され、プログラムに公開キーが挿入されたデジタル署名を使用できます。

于 2011-01-03T11:03:19.790 に答える