使用 PHP 进行串行生成

发布于 2024-09-18 19:53:52 字数 302 浏览 7 评论 0原文

在我作为 PHP 程序员和 C# 编程初学者的日子里,我一直想知道生成独特序列的最佳方法,例如 Microsoft Office 和 Microsoft 操作系统的做法。

有谁有一个很好的指南来指导如何处理这个问题,比如生成唯一序列的重要因素是什么,防止重复等。关于如何创建/验证它们的小例子。

这是我正在谈论的 RFC: https://www.rfc-editor.org/ rfc/rfc1982

Throughout my days as a PHP Programmer and a beginner at C# programming I've always wondered of the best way to generate unique serials such as how Microsoft Office and Microsoft operating systems do.

Does anyone have a good guide to how to handle this, Like what are the important factors in generating the unique serial, prevent duplicates etc. small example on how to create / verify them.

Here is the RFC I'm talking about: https://www.rfc-editor.org/rfc/rfc1982.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

高速公鹿 2024-09-25 19:53:53

我不太确定你想要什么,但是有 http://www .php.net/manual/en/function.uniqid.php 创建唯一标识符。

如果您对它们的创建方式更感兴趣,我建议您查看 PHP 源代码,了解 uniqid 是如何实现的。

I'm not absolutely sure what you want, but there is http://www.php.net/manual/en/function.uniqid.php which creates Unique Identifiers.

If you're more interested in how they are created, I'd advice you to take a look at the PHP source to see how uniqid is implemented.

帅气称霸 2024-09-25 19:53:53

需要更多信息。您是否想要像购买游戏时一样创建序列号,或者您想要一个唯一用户 ID 的序列号。

序列号是否需要存储过期时间/日期等信息?

@nikic - 所有兰德都使用时间作为启动随机生成器的基础。这就是国防部使用熔岩灯的原因。他们有一整间专门放置熔岩灯的房间,用激光照射熔岩灯,随机创建一把独特的钥匙。

编辑:

那么您需要考虑的是您的 C# 应用程序将使用什么方法与您的站点进行通信。

您需要使用 php 创建一个密钥并将其存储在数据库中,以便 C# 可以确认序列号是否正确。

然后,您需要确定 PHP 是否会返回另一个值,以便 C# 应用程序知道密钥是否已通过身份验证。

我在粘贴中所做的是创建公钥和私钥。公钥将提供给用户以验证产品。当他们验证产品或登录系统时,将根据数据库检查公钥,返回值将是私钥。在与我的 C# 程序的所有交互过程中,如果用户需要检查更新或从我的服务器提取信息,私钥将被添加到对服务器的查询中,以确认最终用户是合法的。

我也使用过的另一种方法是上述方法,但添加了额外的检查以确认用户没有共享密钥。 C# 应用程序将获取计算机中处理器的序列号,并在注册应用程序后将其保存到我的数据库中。然后,如果其他人尝试使用相同的公钥但处理器的不同序列号来注册产品,则会抛出错误,他们需要联系支持人员。您可以这样做以允许 5 个不同的机器 ID 或您想要的数量。

创建注册密钥非常简单,因为您实际上只需要创建一个带有偏移量的随机字符串(例如用户名)。

不过,您也可以根据某人提供的名称或公司名称创建注册密钥,并将该算法添加到您的 C# 程序中。其缺点是 C# 源代码可以轻松反编译,可以找到算法来轻松创建注册码,而无需有人实际购买产品。通过添加进行身份验证的服务器,某人生成自己的序列密钥会变得更加困难。

A little more information is needed. Are you wanting to create a serial key like when you purchase a game or are you wanting a serial for a unique user id.

Are the serial numbers needing to store information such as an expiration time/date?

@nikic - all rand's use time as a base for starting the random generator. That is why the DoD uses lava lamps. They have a whole room dedicated to lava lamps and shine lasers on them to randomly create a unique key.

Edited:

Well what you need to take into effect is what method your C# application will use to communicate with your site.

You will need to create a key with php and store it in the database so that the C# can confirm the serial is a correct one.

You then need to figure out if the PHP will return another value so the C# application is aware the key was authenticated or not.

What I have done in the pasted was create a public key and private key. The public key would be given to the user to validate the product. When they validate the product or login to the system the public key would be checked against the database and the return value would be the private key. During all the interactions with my C# program should the user need to check for updates or pull information from my server the private key would be added to the query to the server to confirm the end user was legit.

Another method I have also used is the above but added additional checking to confirm the user wasn't sharing the key. The C# application would obtain the serial number of the processor in the computer and upon registration of the application it would save it to my database. Then if someone else tried to register the product with the same Public key but different serial number of the processor it would throw an error and they would need to contact support. You can do this to allow 5 different machine ID's or how every many you want.

Creating the registration key is very simple as you really only need to create a random string with an offset (such as the users name).

You could also however create a registration key based on a name or company name that someone provides and add that algorithm to your C# program. The downside to that is C# source code can be decompiled easily the algorithm can be found to create a registration code easily without someone actually paying for the product. By adding in a server that does the authentication it is much more difficult for someone to generate their own serial key.

忆梦 2024-09-25 19:53:52

这是我们的解决方案。它根据有效的 IPv4、用户 ID(或任何有意义的整数)或文本字符串生成具有可配置大小/长度和可选后缀的密钥。它还避免标准结果中出现不明确的字符 (i,1,l,0,o,O)。

我们将用户 ID 附加到许可证中,稍后可以将该部分转换回以 10 为基数的整数,并检查它对于使用该许可证的用户帐户是否有效。

$license = generate_license();
// YF6G2-HJQEZ-8JZKY-8C8ZN

$license = generate_license(123456);
// ZJK82N-8GA5AR-ZSPQVX-2N9C

$license = generate_license($_SERVER['REMOTE_ADDR']);
// M9H7FP-996BNB-77Y9KW-ARUP4

$license = generate_license('my text suffix');
// Q98K2F-THAZWG-HJ8R56-MY-TEXT-SUFFIX

我们在创建时会检查数据库中的唯一性,但是将 IP/用户 ID 与随机性结合使用,重复的可能性几乎为零。

/**
* Generate a License Key.
* Optional Suffix can be an integer or valid IPv4, either of which is converted to Base36 equivalent
* If Suffix is neither Numeric or IPv4, the string itself is appended
*
* @param   string  $suffix Append this to generated Key.
* @return  string
*/
function generate_license($suffix = null) {
    // Default tokens contain no "ambiguous" characters: 1,i,0,o
    if(isset($suffix)){
        // Fewer segments if appending suffix
        $num_segments = 3;
        $segment_chars = 6;
    }else{
        $num_segments = 4;
        $segment_chars = 5;
    }
    $tokens = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
    $license_string = '';
    // Build Default License String
    for ($i = 0; $i < $num_segments; $i++) {
        $segment = '';
        for ($j = 0; $j < $segment_chars; $j++) {
            $segment .= $tokens[rand(0, strlen($tokens)-1)];
        }
        $license_string .= $segment;
        if ($i < ($num_segments - 1)) {
            $license_string .= '-';
        }
    }
    // If provided, convert Suffix
    if(isset($suffix)){
        if(is_numeric($suffix)) {   // Userid provided
            $license_string .= '-'.strtoupper(base_convert($suffix,10,36));
        }else{
            $long = sprintf("%u\n", ip2long($suffix),true);
            if($suffix === long2ip($long) ) {
                $license_string .= '-'.strtoupper(base_convert($long,10,36));
            }else{
                $license_string .= '-'.strtoupper(str_ireplace(' ','-',$suffix));
            }
        }
    }
    return $license_string;
}

Here's our solution. It generates a key with a configurable size/length and optional suffix based on a valid IPv4, Userid (or any meaningful integer), or text string. It also avoids ambiguous characters (i,1,l,0,o,O) in the standard result.

We append the Userid in the license, and can later convert that portion back to a base10 integer and check if it's valid against the user account that is using the license.

$license = generate_license();
// YF6G2-HJQEZ-8JZKY-8C8ZN

$license = generate_license(123456);
// ZJK82N-8GA5AR-ZSPQVX-2N9C

$license = generate_license($_SERVER['REMOTE_ADDR']);
// M9H7FP-996BNB-77Y9KW-ARUP4

$license = generate_license('my text suffix');
// Q98K2F-THAZWG-HJ8R56-MY-TEXT-SUFFIX

We do check uniqueness in the database when created, but using the IP/Userid in conjunction with the randomness, the likelihood of duplicates is virtually zero.

/**
* Generate a License Key.
* Optional Suffix can be an integer or valid IPv4, either of which is converted to Base36 equivalent
* If Suffix is neither Numeric or IPv4, the string itself is appended
*
* @param   string  $suffix Append this to generated Key.
* @return  string
*/
function generate_license($suffix = null) {
    // Default tokens contain no "ambiguous" characters: 1,i,0,o
    if(isset($suffix)){
        // Fewer segments if appending suffix
        $num_segments = 3;
        $segment_chars = 6;
    }else{
        $num_segments = 4;
        $segment_chars = 5;
    }
    $tokens = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
    $license_string = '';
    // Build Default License String
    for ($i = 0; $i < $num_segments; $i++) {
        $segment = '';
        for ($j = 0; $j < $segment_chars; $j++) {
            $segment .= $tokens[rand(0, strlen($tokens)-1)];
        }
        $license_string .= $segment;
        if ($i < ($num_segments - 1)) {
            $license_string .= '-';
        }
    }
    // If provided, convert Suffix
    if(isset($suffix)){
        if(is_numeric($suffix)) {   // Userid provided
            $license_string .= '-'.strtoupper(base_convert($suffix,10,36));
        }else{
            $long = sprintf("%u\n", ip2long($suffix),true);
            if($suffix === long2ip($long) ) {
                $license_string .= '-'.strtoupper(base_convert($long,10,36));
            }else{
                $license_string .= '-'.strtoupper(str_ireplace(' ','-',$suffix));
            }
        }
    }
    return $license_string;
}
你的他你的她 2024-09-25 19:53:52

如果您的应用程序有一个返回到服务器的连接,那么这很简单,只需生成随机令牌,将它们存储在数据库中,并要求应用程序在运行之前与服务器进行检查。然而,有些客户可能会觉得这个要求无法接受。 (我个人永远不会购买任何需要互联网激活的软件。我想购买软件,而不是租用它。)

对于具有真实性可检查性而无需连接回服务器的密钥:

  1. 在服务器上,生成一个随机唯一令牌。

  2. 使用公钥加密方案(例如RSA)使用私有密钥对令牌进行签名对服务器保密的密钥。

  3. 使用某种二进制到文本方案(例如,base64,或仅使用 2-9A-KMNP-Z 符号以实现更安全的可打字性)对唯一令牌和签名进行编码。编码组合是您的密钥或序列号。

  4. 在应用程序的每个副本中嵌入与服务器私钥匹配的公钥,并让应用程序在安装时提示输入密钥。它可以拆分唯一令牌和签名,并使用公钥来验证签名对于该令牌是否有效。

这种方法需要您将一些加密库与您的软件捆绑在一起,并且为了安全起见,大多数算法都使用相当长的签名,这将使您的序列号输入起来非常繁琐。如果您希望用户复制并粘贴这些数字,那也没关系。

由于这些原因,许多软件包使用安全性较低的密钥验证方案,其中检查算法完全内置于客户端中。这可能是一个哈希值,例如。序列号的最后四个十六进制数字必须与序列号其余部分的 SHA1 哈希值的最低两个字节以及“秘密”密钥相匹配。然而,由于密钥必须捆绑在应用程序中,因此黑客有可能查看应用程序代码并提取“秘密”密钥,从而允许他们编写自己的密钥生成器。

这就是为什么对于某些程序,您会看到可用的“密钥生成器”:该应用程序使用了安全性较低的检查算法,该算法在应用程序中留下了足够的信息,以允许破解者重现密钥制作过程。对于基于公钥加密或互联网激活的更安全设置的程序,您通常会看到应用程序的“破解”版本,其中检查代码已被更改/删除。

...这表明无论你做什么,你仍然无法强制诚实。所以不用太担心它。

If your application has a connection back to the server it's trivial, just generate random tokens, store them in a database, and require the application to check back with the server before running. However some customers may find this requirement unacceptable. (I personally will never buy any software with an internet activation requirement. I want to buy software, not rent it.)

For keys with genuineness checkability without having to have a connection back to the server:

  1. On the server, generate a random unique token.

  2. Use a public key cryptography scheme (eg. RSA) to sign the token with a private key that is kept secret to the server.

  3. Encode the unique token and the signature together using some binary-to-text scheme (eg. base64, or just using 2-9A-KMNP-Z symbols for safer typability). The encoded combination is your key or serial.

  4. Embed the public key matching the server's private key in each copy of the application, and have the application prompt for a key at install time. It can split the unique token and the signature, and use the public key to verify that the signature is valid for that token.

This approach requires you bundle some crypto libraries with your software, and most algorithms use quite long signatures, for security, which will make your serial numbers pretty tedious to type in. It's OK if you expect users to copy-and-paste the numbers.

For these reasons many software packages use a less-secure key validation scheme where the checking algorithm is wholly built into the client. This might be a hash, eg. the last four hex digits of the serial must match the lowest two bytes of the SHA1 hash of the rest of the serial combined with a ‘secret’ key. However, since the key has to be bundled in the application, it's possible for a hacker to look at the app code and extract the ‘secret’ key, allowing them to write their own key generators.

That's why for some programs you will see ‘keygens’ available: the app used a less-secure checking algo which left enough information in the application to allow the cracker to reproduce the key-making process. For programs with a more secure setup based on public-key crypto or internet activation, you typically see a ‘cracked’ version of the application instead, where the checking code has been altered/removed.

...which kind of demonstrates that whatever you do, you're still not going to be able to enforce honesty. So don't worry too much about it.

梦年海沫深 2024-09-25 19:53:52

我的解决方案

implode( '-', str_split( substr( strtoupper( md5( time() . rand( 1000, 9999 ) ) ), 0, 20 ), 4 ) );

My solution

implode( '-', str_split( substr( strtoupper( md5( time() . rand( 1000, 9999 ) ) ), 0, 20 ), 4 ) );
再可℃爱ぅ一点好了 2024-09-25 19:53:52

如果您想集中创建随机序列号,只需从数字和字母数组中随机获取就足够了。每次使用 mt_rand(0, count($poolArray)) 获取一个新索引,然后将该字符添加到序列中,直到达到 12 个为止。

从如此大的池(26 个字母 + 10 个数字)中随机生成它们几乎可以确保您没有重复项,但您始终可以在存储新字母之前检查现有字母。

如果您有 36 个可能的字符,并且您从中随机选择 12 个来制作连续剧,则有 36*36*36*...*36 = 36^12 = 4738381338321616896 个可能的字符串。

$pool = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$countPool = strlen($pool) - 1;
$totalChars = 12;

$serial = '' ;
for ($i = 0 ; $i < $totalChars ; $i++) {
    $currIndex = mt_rand(0, $countPool) ;
    $currChar = $pool[$currIndex] ;
    $serial .= $currChar ;
}

将它们与您的代码一起分发或让您的程序检查有效的序列号是另一个问题。

If you want to create random serial numbers centrally, just fetching randomly from an array of numbers and letters should be enough. Use mt_rand(0, count($poolArray)) to get a new index every time and just add that character to your serial until you have say 12 of them.

Generating them in random from such a large pool (26 letters + 10 digits) will almost make sure that you don't have duplicates, but you can always just check for existing ones before you store the new one.

If you have 36 possible characters, and you pick 12 randomly from them to make your serials, that's 36*36*36*...*36 = 36^12 = 4738381338321616896 possible strings.

$pool = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$countPool = strlen($pool) - 1;
$totalChars = 12;

$serial = '' ;
for ($i = 0 ; $i < $totalChars ; $i++) {
    $currIndex = mt_rand(0, $countPool) ;
    $currChar = $pool[$currIndex] ;
    $serial .= $currChar ;
}

Distributing them with your code or having your program check for a valid serial is another issue.

看轻我的陪伴 2024-09-25 19:53:52

这是 GDP 答案的修改版本,其中包括可以验证的集成校验和。 注意:虽然此方法增加了一层验证,但安全性并不高。要获得更安全的解决方案,请考虑使用加密签名。

这是示例输出:

$license = generate_license();
// PKFAT-NGLNM-SQQ7Q-PB4JN-E7OG0

$license = generate_license(123456);
// DBUK2-T7WCP-JH48A-UCC5R-2N9C-3T7PH

$license = generate_license($_SERVER['REMOTE_ADDR']);
// 9ZD49-BRRWK-NBV4J-GJJ5G-196KW24-7WRTJ

$license = generate_license('my text suffix');
// 8DRAN-FRP8A-2KK2F-ASHFT-MY-TEXT-SUFFIX-9LWUO

以下是验证许可证密钥的方法:

$validLicense = '5258U-NRDBN-72XTC-X9QQ4-2KRDA';
$invalidLicense = '5258U-NRDBN-72XTC-X9QQ4-3KRDB';

var_dump(verify_license($validLicense));
// bool(true)

var_dump(verify_license($invalidLicense));
// bool(false)

为了启用验证,我们可以在许可证密钥中引入校验和。该校验和可以是许可证密钥某些片段的简单散列,附加到许可证本身。在验证过程中,我们可以根据许可证密钥再次计算校验和,并将其与原始校验和进行比较。这是生成许可证密钥的代码:

/**
 * Generate a License Key with Alphanumeric Checksum of Segment Length.
 *
 * This function generates a license key and appends an alphanumeric checksum
 * whose length matches the segment length for better uniformity.
 *
 * @param   string  $suffix Optional. Append this to the generated key.
 * @return  string  License key with appended checksum.
 */
function generate_license($suffix = null) {
    // Set default number of segments and segment characters
    $num_segments = 4;
    $segment_chars = 5;

    // Tokens used for license generation (ambiguous characters removed)
    $tokens = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';

    // Initialize license string
    $license_string = '';

    // Build default license string
    for ($i = 0; $i < $num_segments; $i++) {
        $segment = '';
        for ($j = 0; $j < $segment_chars; $j++) {
            $segment .= $tokens[rand(0, strlen($tokens) - 1)];
        }
        $license_string .= $segment;

        // Add separator unless at the last segment
        if ($i < ($num_segments - 1)) {
            $license_string .= '-';
        }
    }

    // Handle optional suffix
    if (isset($suffix)) {
        if (is_numeric($suffix)) {
            $license_string .= '-' . strtoupper(base_convert($suffix, 10, 36));
        } else {
            $long = sprintf("%u", ip2long($suffix), true);
            if ($suffix === long2ip($long)) {
                $license_string .= '-' . strtoupper(base_convert($long, 10, 36));
            } else {
                $license_string .= '-' . strtoupper(str_ireplace(' ', '-', $suffix));
            }
        }
    }

    // Generate alphanumeric checksum and append it to the license string
    $checksum = strtoupper(base_convert(md5($license_string), 16, 36));

    // Adjust the length of the checksum to match segment_chars
    $checksum = substr($checksum, 0, $segment_chars);

    $license_string .= '-' . $checksum;

    return $license_string;
}

您可以使用此函数验证许可证密钥:

/**
 * Verify a License Key with Alphanumeric Checksum of Segment Length.
 *
 * This function verifies a license key by checking its alphanumeric checksum
 * whose length matches the segment length.
 *
 * @param   string  $license License key to verify.
 * @return  bool    True if valid, false otherwise.
 */
function verify_license($license) {
    // Split the license key into segments by dash
    $segments = explode('-', $license);

    // Extract the checksum from the last segment
    $checksum = end($segments);

    // Remove checksum to get the base license string
    array_pop($segments);
    $license_base = implode('-', $segments);

    // Compute checksum for the base license string
    $computed_checksum = strtoupper(base_convert(md5($license_base), 16, 36));

    // Adjust the length of the computed checksum to match the original
    $computed_checksum = substr($computed_checksum, 0, strlen($checksum));

    // Verify by comparing the checksums
    return $checksum === $computed_checksum;
}```

Here's a modified version of GDP's answer, which includes an integrated checksum which can be verified. Note: Although this method adds a layer of validation, it is not highly secure. For a more secure solution, consider using cryptographic signatures.

This is the sample output:

$license = generate_license();
// PKFAT-NGLNM-SQQ7Q-PB4JN-E7OG0

$license = generate_license(123456);
// DBUK2-T7WCP-JH48A-UCC5R-2N9C-3T7PH

$license = generate_license($_SERVER['REMOTE_ADDR']);
// 9ZD49-BRRWK-NBV4J-GJJ5G-196KW24-7WRTJ

$license = generate_license('my text suffix');
// 8DRAN-FRP8A-2KK2F-ASHFT-MY-TEXT-SUFFIX-9LWUO

and here's how you can validate the license key:

$validLicense = '5258U-NRDBN-72XTC-X9QQ4-2KRDA';
$invalidLicense = '5258U-NRDBN-72XTC-X9QQ4-3KRDB';

var_dump(verify_license($validLicense));
// bool(true)

var_dump(verify_license($invalidLicense));
// bool(false)

To enable verification, we can introduce a checksum into the license key. This checksum can be a simple hash of some segments of the license key, appended to the license itself. During verification, we can compute the checksum again from the license key and compare it to the original checksum. This is the code to generate the license key:

/**
 * Generate a License Key with Alphanumeric Checksum of Segment Length.
 *
 * This function generates a license key and appends an alphanumeric checksum
 * whose length matches the segment length for better uniformity.
 *
 * @param   string  $suffix Optional. Append this to the generated key.
 * @return  string  License key with appended checksum.
 */
function generate_license($suffix = null) {
    // Set default number of segments and segment characters
    $num_segments = 4;
    $segment_chars = 5;

    // Tokens used for license generation (ambiguous characters removed)
    $tokens = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';

    // Initialize license string
    $license_string = '';

    // Build default license string
    for ($i = 0; $i < $num_segments; $i++) {
        $segment = '';
        for ($j = 0; $j < $segment_chars; $j++) {
            $segment .= $tokens[rand(0, strlen($tokens) - 1)];
        }
        $license_string .= $segment;

        // Add separator unless at the last segment
        if ($i < ($num_segments - 1)) {
            $license_string .= '-';
        }
    }

    // Handle optional suffix
    if (isset($suffix)) {
        if (is_numeric($suffix)) {
            $license_string .= '-' . strtoupper(base_convert($suffix, 10, 36));
        } else {
            $long = sprintf("%u", ip2long($suffix), true);
            if ($suffix === long2ip($long)) {
                $license_string .= '-' . strtoupper(base_convert($long, 10, 36));
            } else {
                $license_string .= '-' . strtoupper(str_ireplace(' ', '-', $suffix));
            }
        }
    }

    // Generate alphanumeric checksum and append it to the license string
    $checksum = strtoupper(base_convert(md5($license_string), 16, 36));

    // Adjust the length of the checksum to match segment_chars
    $checksum = substr($checksum, 0, $segment_chars);

    $license_string .= '-' . $checksum;

    return $license_string;
}

You can verify the license key using this function:

/**
 * Verify a License Key with Alphanumeric Checksum of Segment Length.
 *
 * This function verifies a license key by checking its alphanumeric checksum
 * whose length matches the segment length.
 *
 * @param   string  $license License key to verify.
 * @return  bool    True if valid, false otherwise.
 */
function verify_license($license) {
    // Split the license key into segments by dash
    $segments = explode('-', $license);

    // Extract the checksum from the last segment
    $checksum = end($segments);

    // Remove checksum to get the base license string
    array_pop($segments);
    $license_base = implode('-', $segments);

    // Compute checksum for the base license string
    $computed_checksum = strtoupper(base_convert(md5($license_base), 16, 36));

    // Adjust the length of the computed checksum to match the original
    $computed_checksum = substr($computed_checksum, 0, strlen($checksum));

    // Verify by comparing the checksums
    return $checksum === $computed_checksum;
}```
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文