使用 PHP 生成串行

Serial generation with PHP

提问人:RobertPitt 提问时间:9/11/2010 最后编辑:CommunityRobertPitt 更新时间:10/16/2023 访问量:29250

问:

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

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

这是我说的RFC:https://www.rfc-editor.org/rfc/rfc1982

php 许可证密钥

评论


答:

0赞 halfdan 9/11/2010 #1

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

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

评论

0赞 NikiC 9/11/2010
Uniqid 是完全可预测的。实际上,它只是它产生的微时间的表示。这绝不是随机的。
0赞 Bot 9/11/2010 #2

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

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

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

编辑:

好吧,您需要生效的是您的 C# 应用程序将使用什么方法与您的站点进行通信。

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

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

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

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

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

但是,也可以根据某人提供的名称或公司名称创建注册密钥,并将该算法添加到 C# 程序中。这样做的缺点是 C# 源代码可以很容易地反编译,该算法可以很容易地创建注册码,而无需有人实际为产品付费。通过添加执行身份验证的服务器,某人生成自己的序列号要困难得多。

评论

0赞 RobertPitt 9/11/2010
假设我有一个产品,例如一个应用程序,这个应用程序需要一个串行系统,我想在 PHP 中生成一个可以在 C# 中检查和验证的串行,我必须在脚本中采用哪些因素才能生成和验证 A 一个密钥,而不管是什么解释器。
0赞 Bot 9/11/2010
我编辑了我的答案,以显示我在粘贴中所做的一些示例。
22赞 bobince 9/11/2010 #3

如果您的应用程序与服务器有连接,这很简单,只需生成随机令牌,将它们存储在数据库中,并要求应用程序在运行之前与服务器进行核对。但是,某些客户可能会发现此要求不可接受。(我个人永远不会购买任何有互联网激活要求的软件。我想购买软件,而不是租用它。

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

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

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

  3. 使用一些二进制到文本方案(例如 base64,或仅使用符号以实现更安全的可类型性)将唯一标记和签名编码在一起。编码的组合是密钥或序列号。2-9A-KMNP-Z

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

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

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

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

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

评论

0赞 RobertPitt 9/11/2010
完美,正是我正在寻找:)的信息类型,很棒的阅读。+1
1赞 Bot 9/12/2010
:-/这正是我说的。
1赞 Fanis Hatzidakis 9/11/2010 #4

如果要集中创建随机序列号,只需从数字和字母数组中随机获取就足够了。用于每次获取一个新索引,只需将该字符添加到您的序列中,直到您说出其中的 12 个。mt_rand(0, count($poolArray))

从如此大的池中随机生成它们(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 ;
}

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

评论

0赞 Zyoo 2/2/2013
对不起,为什么给我结果101000100100而如果我明确分配 35,它的工作原理为 3UNPWR3GWHQP,如果我分配 36,它会使用单位化偏移量count($pool)
0赞 Fanis Hatzidakis 2/2/2013
@yolapop你是绝对正确的。我本来想$pool写成一个数组,但不知何故最终把它写成一个字符串。把它看作一个数组,方法论是有效的。
29赞 GDP 4/2/2015 #5

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

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

$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/Userid 与随机性结合使用时,重复的可能性几乎为零。

/**
* 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;
}

评论

1赞 Mike 1/10/2019
你能分享验证功能吗?
14赞 Dez 8/18/2015 #6

我的解决方案

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

评论

0赞 Arun Basil Lal 11/20/2017
这是非常优雅的!
0赞 Mr.Web 1/2/2018
好!给定序列号,您可以解码回日期吗?
0赞 Henry Howeson 10/26/2018
@Mr.Web 没有,因为 md5 哈希函数几乎不可能逆转
0赞 Bradley 10/16/2023 #7

这是 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;
}```