检查生成的许可证是否有效

Ben*_*wis 9 php security function license-key

我有一个PHP脚本生成一些将用作许可证密钥的字符串:

function KeyGen(){
     $key = md5(microtime());
     $new_key = '';
     for($i=1; $i <= 25; $i ++ ){
               $new_key .= $key[$i];
               if ( $i%5==0 && $i != 25) $new_key.='-';
     }
  return strtoupper($new_key);
  }
$x = 0;
while($x <= 10) {
  echo KeyGen();
  echo "<br />";
$x++; 
}
Run Code Online (Sandbox Code Playgroud)

运行一次脚本后,我得到了这些:

8B041-EC7D2-0B9E3-09846-E8C71
C8D82-514B9-068BC-8BF80-05061
A18A3-E05E5-7DED7-D09ED-298C4
FB1EC-C9844-B9B20-ADE2F-0858F
E9AED-945C8-4BAAA-6938D-713ED
4D284-C5A3B-734DF-09BD6-6A34C
EF534-3BAE4-860B5-D3260-1CEF8
D84DB-B8C72-5BDEE-1B4FE-24E90
93AF2-80813-CD66E-E7A5E-BF0AE
C3397-93AA3-6239C-28D9F-7A582
D83B8-697C6-58CD1-56F1F-58180
Run Code Online (Sandbox Code Playgroud)

我现在要做的是更改它,以便我有另一个函数来检查是否已使用我的脚本生成密钥.目前,我在想的是设置$key一个特定字符串的MD5(例如test),但当然,它返回所有字符串相同.

有人可以帮忙吗?

Bab*_*aba 14

注意:

此解决方案假设您希望您的许可证密钥始终处于fixed format(见下文)并且仍然存在self authenticated

  FORMAT : XXXXX-XXXXX-XXXXX-XXXXX-XXXX
Run Code Online (Sandbox Code Playgroud)

如果不是这种情况,请参考@ircmaxell更好的解决方案

介绍

自我认证的序列是棘手的解决方案,因为:

  • 限量尺寸的系列
  • 它需要在没有数据库或任何存储的情况下自我验证
  • 如果私钥被泄露...它可以很容易地被撤销

$option = new CheckProfile();
$option->name = "My Application"; // Application Name
$option->version = 0.9; // Application Version
$option->username = "Benedict Lewis"; // you can limit the key to per user
$option->uniqid = null; // add if any


$checksum = new Checksum($option);
$key = $checksum->generate();
var_dump($key, $checksum->check($key));
Run Code Online (Sandbox Code Playgroud)

产量

string '40B93-C7FD6-AB5E6-364E2-3B96F' (length=29)
boolean true
Run Code Online (Sandbox Code Playgroud)

请注意,选项中的任何修改都会更改密钥并使其无效;

检查碰撞

我刚刚进行了这个简单的测试

set_time_limit(0);

$checksum = new Checksum($option);
$cache = array();
$collision = $error = 0;
for($i = 0; $i < 100000; $i ++) {
    $key = $checksum->generate();
    isset($cache[$key]) and $collision ++;
    $checksum->check($key) or $error ++;
    $cache[$key] = true;
}

printf("Fond %d collision , %d Errors  in 100000 entries", $collision, $error);
Run Code Online (Sandbox Code Playgroud)

产量

  Fond 0 collision , 0 Errors in 100000 entries 
Run Code Online (Sandbox Code Playgroud)

更好的安全

默认情况下,脚本使用sha1PHP有很多更好的方法hash functions可以通过以下代码获得

print_r(hash_algos());
Run Code Online (Sandbox Code Playgroud)

$checksum = new Checksum($option, null, "sha512");
Run Code Online (Sandbox Code Playgroud)

使用的类

class Checksum {
    // Used used binaray in Hex format
    private $privateKey = "ec340029d65c7125783d8a8b27b77c8a0fcdc6ff23cf04b576063fd9d1273257"; // default
    private $keySize = 32;
    private $profile;
    private $hash = "sha1";

    function __construct($option, $key = null, $hash = "sha1") {
        $this->profile = $option;
        $this->hash = $hash;

        // Use Default Binary Key or generate yours
        $this->privateKey = ($key === null) ? pack('H*', $this->privateKey) : $key;
        $this->keySize = strlen($this->privateKey);
    }

    private function randString($length) {
        $r = 0;
        switch (true) {
            case function_exists("openssl_random_pseudo_bytes") :
                $r = bin2hex(openssl_random_pseudo_bytes($length));
                break;
            case function_exists("mcrypt_create_ivc") :
            default :
                $r = bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));
                break;
        }
        return strtoupper(substr($r, 0, $length));
    }

    public function generate($keys = false) {
        // 10 ramdom char
        $keys = $keys ?  : $this->randString(10);
        $keys = strrev($keys); // reverse string

        // Add keys to options
        $this->profile->keys = $keys;

        // Serialise to convert to string
        $data = json_encode($this->profile);

        // Simple Random Chr authentication
        $hash = hash_hmac($this->hash, $data, $this->privateKey);
        $hash = str_split($hash);

        $step = floor(count($hash) / 15);
        $i = 0;

        $key = array();
        foreach ( array_chunk(str_split($keys), 2) as $v ) {
            $i = $step + $i;
            $key[] = sprintf("%s%s%s%s%s", $hash[$i ++], $v[1], $hash[$i ++], $v[0], $hash[$i ++]);
            $i ++; // increment position
        }
        return strtoupper(implode("-", $key));
    }

    public function check($key) {
        $key = trim($key);
        if (strlen($key) != 29) {
            return false;
        }
        // Exatact ramdom keys
        $keys = implode(array_map(function ($v) {
            return $v[3] . $v[1];
        }, array_map("str_split", explode("-", $key))));

        $keys = strrev($keys); // very important
        return $key === $this->generate($keys);
    }
}
Run Code Online (Sandbox Code Playgroud)


irc*_*ell 14

处理这个有三种基本方法.如何操作将取决于您生成的密钥数量,以及能够在以后使密钥无效的重要性.你选择哪一个取决于你.

选项1:服务器数据库存储

当服务器生成密钥(如使用您的算法)时,将其存储在数据库中.然后,您需要做的就是检查密钥是否在数据库中.

请注意,您的算法需要比您提供的更多的熵.当前时间戳是足够的.相反,使用强随机性:

$key = mcrypt_create_iv($length_needed, MCRYPT_DEV_URANDOM);
Run Code Online (Sandbox Code Playgroud)

或者,如果你没有mcrypt:

$key = openssl_random_pseudo_bytes($length_needed);
Run Code Online (Sandbox Code Playgroud)

或者,如果您没有mcrypt和openssl,请使用

请注意,md5返回十六进制输出(a-f0-9),其中所有上述返回完整的随机二进制字符串(字符0 - 255).无论是base64_encode()它还是bin2hex()它.

优点:

  • 易于实施
  • 可以"拒绝"在以后发布密钥
  • 不可能伪造一把新钥匙

缺点:

  • 每个密钥需要持久存储
  • 可能无法很好地扩展
  • 需要"密钥服务器"来验证密钥

选项2:签名密钥

基本上,您生成一个强大的随机密钥(从这里称为私钥),并将其存储在您的服务器上.然后,在生成许可证密钥时,生成随机blob,然后HMAC使用私钥对其进行签名,并使该块的许可证成为其中的一部分.这样,您就不需要存储每个密钥.

function create_key($private_key) {
    $rand = mcrypt_create_iv(10, MCRYPT_DEV_URANDOM);
    $signature = substr(hash_hmac('sha256', $rand, $private_key, true), 0, 10);
    $license = base64_encode($rand . $signature);
    return $license;
}

function check_key($license, $private_key) {
    $tmp = base64_decode($license);
    $rand = substr($tmp, 0, 10);
    $signature = substr($tmp, 10);
    $test = substr(hash_hmac('sha256', $rand, $private_key, true), 0, 10);
    return $test === $signature;
}
Run Code Online (Sandbox Code Playgroud)

优点:

  • 易于实施
  • 不需要持久存储
  • 琐碎的规模

缺点:

  • 不能单独"停用"键
  • 需要存储"私钥"
  • 需要"密钥服务器"来验证密钥.

选项3:公钥加密

基本上,您生成公钥/私钥对.您将公钥嵌入应用程序中.然后,您生成一个密钥(类似于上面的"签名密钥"),但不是使用HMAC签名对其进行签名,而是使用私钥对其进行签名.

这样,应用程序(具有公钥)可以直接验证签名,而无需回叫您的服务器.

function create_key($private_key) {
    $rand = mcrypt_create_iv(10, MCRYPT_DEV_URANDOM);

    $pkeyid = openssl_get_privatekey($private_key);
    openssl_sign($rand, $signature, $pkeyid);
    openssl_free_key($pkeyid);

    $license = base64_encode($rand . $signature);
    return $license;
}

function check_key($license, $public_key) {
    $tmp = base64_decode($license);
    $rand = substr($tmp, 0, 10);
    $signature = substr($tmp, 10);

    $pubkeyid = openssl_get_publickey($public_key);
    $ok = openssl_verify($rand, $signature, $pubkeyid);
    openssl_free_key($pubkeyid);

    return $ok === 1;
}
Run Code Online (Sandbox Code Playgroud)

优点:

  • 易于实施
  • 不需要持久存储
  • 琐碎的规模
  • 不需要"密钥服务器"来验证密钥

缺点:

  • 不能单独"停用"键
  • 需要存储"私钥"

  • 非常感谢你的回答,遗憾的是我无法接受巴巴和你的回答. (2认同)