Php 检查生成的许可证是否有效
我有一个PHP脚本,它生成一些字符串,这些字符串将用作许可证密钥:Php 检查生成的许可证是否有效,php,security,function,license-key,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.='-'; }
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++;
}
我现在要做的是修改它,这样我就有了另一个函数来检查这个键是否是用我的脚本生成的。目前,我的想法是将$key
设置为一个特定字符串的MD5(例如,test
),当然,这会返回相同的所有字符串
有人能帮忙吗?在您创建这些密钥时,将它们存储在数据库中。稍后将它们与数据库行匹配,然后瞧。这将完成。您实际需要的是类似于部分密钥验证的算法 请参阅本文了解工作原理并将其移植到PHP
请注意,使用此算法获得重复密钥并非不可能,不太可能,但中奖也是如此。您必须将密钥存储在数据库或文件中,以检查它是否存在。注意: 此解决方案假设您希望您的许可证密钥始终为
固定格式
(见下文),并且仍然是自验证的
FORMAT : XXXXX-XXXXX-XXXXX-XXXXX-XXXX
如果情况并非如此,请参阅@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));
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);
}
}
输出
string '40B93-C7FD6-AB5E6-364E2-3B96F' (length=29)
boolean true
Fond 0 collision , 0 Errors in 100000 entries
请注意,选项中的任何修改都会更改密钥并使其无效
检查碰撞
$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));
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);
}
}
我刚刚做了一个简单的测试
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);
更好的安全性
$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));
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);
}
}
默认情况下,脚本使用sha1
,但是PHP
有很多更好的散列函数
,您可以通过以下代码获得
print_r(hash_algos());
范例
$checksum = new Checksum($option, null, "sha512");
使用的类
$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));
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);
}
}
有三种基本的处理方法。如何操作将取决于生成的密钥数量,以及能够在以后使密钥失效的重要性。你选择哪一个取决于你自己 选项1:服务器数据库存储 当服务器生成密钥时(如使用算法),您将其存储在数据库中。然后,您需要做的就是检查密钥是否在数据库中 注意您的算法需要的熵比您提供的要多得多。当前时间戳不足。相反,使用强随机性:
$key = mcrypt_create_iv($length_needed, MCRYPT_DEV_URANDOM);
或者,如果您没有mcrypt:
$key = openssl_random_pseudo_bytes($length_needed);
或者,如果您没有mcrypt和openssl,请使用
请注意,md5
返回一个十六进制输出(a-f0-9),其中所有上述内容都返回完整的随机二进制字符串(字符0-255)。所以要么base64\u encode()
it,要么bin2hex()
it
赞成的意见:
- 易于实现
- 能否在以后的日期“停用”已颁发的密钥
- 不可能伪造一把新钥匙
- 每个密钥需要持久存储
- 可能没有那么好的伸缩性
- 需要“密钥服务器”来验证密钥
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;
}
赞成的意见:
- 易于实现
- 不需要持久存储
- 微不足道的
- 无法“停用”单个密钥
- 需要存储“私钥”
- 需要“密钥服务器”来验证密钥
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;
}
赞成的意见:
- 易于实现
- 不需要持久存储
- 微不足道的
- 不需要“密钥服务器”来验证密钥
- 无法“停用”单个密钥
- 需要存储“私钥”
$mykey=KeyGen()
@nvanesch我也知道这一点。不过谢谢你提到:)这很有趣。。让我看看我能想出什么我知道最好的方法是存储在数据库中,但是我正在尝试找到一种不用存储的方法,就像Adobe产品在离线时也可以激活一样。谢谢。看起来将Delphi转换成PHP需要做很多工作,所以我仍然在寻找另一种选择,但如果真的需要,我会这么做。通过默默无闻实现安全性是最好的。还有,滥用姓名(你所说的salt不是salt,而是私钥,因为它的有效性与它的保密性有关)。非常感谢你的回答,这正是我所需要的。你能举例说明这种算法在公共场合被使用或审查的安全性吗?它看起来真的很奇怪,而且是土生土长的(尤其是$hash1
和$hash2
部分。我的意思是,为什么crc32()
?看起来