I've made this for generating license keys & later checking if they're OK and valid.
A key is generated per-user and has an expiration time. That works fine.
Could anyone please check if it looks secure - as in, hard to crack without tampering with the verification code?
class LicenseException extends Exception{}
/**
* Fix non-ascii characters and normalize name
*/
function sanitizeName($name)
{
$translit = Array(
'ä' => 'a', 'Ä' => 'A', 'á' => 'a', 'Á' => 'A', 'à' => 'a', 'À' => 'A',
'ã' => 'a', 'Ã' => 'A', 'â' => 'a', 'Â' => 'A', 'č' => 'c', 'Č' => 'C',
'ć' => 'c', 'Ć' => 'C', 'ď' => 'd', 'Ď' => 'D', 'ě' => 'e', 'Ě' => 'E',
'é' => 'e', 'É' => 'E', 'ë' => 'e', 'Ë' => 'E', 'è' => 'e', 'È' => 'E',
'ê' => 'e', 'Ê' => 'E', 'í' => 'i', 'Í' => 'I', 'ï' => 'i', 'Ï' => 'I',
'ì' => 'i', 'Ì' => 'I', 'î' => 'i', 'Î' => 'I', 'ľ' => 'l', 'Ľ' => 'L',
'ĺ' => 'l', 'Ĺ' => 'L', 'ń' => 'n', 'Ń' => 'N', 'ň' => 'n', 'Ň' => 'N',
'ñ' => 'n', 'Ñ' => 'N', 'ó' => 'o', 'Ó' => 'O', 'ö' => 'o', 'Ö' => 'O',
'ô' => 'o', 'Ô' => 'O', 'ò' => 'o', 'Ò' => 'O', 'õ' => 'o', 'Õ' => 'O',
'ő' => 'o', 'Ő' => 'O', 'ř' => 'r', 'Ř' => 'R', 'ŕ' => 'r', 'Ŕ' => 'R',
'š' => 's', 'Š' => 'S', 'ś' => 's', 'Ś' => 'S', 'ť' => 't', 'Ť' => 'T',
'ú' => 'u', 'Ú' => 'U', 'ů' => 'u', 'Ů' => 'U', 'ü' => 'u', 'Ü' => 'U',
'ù' => 'u', 'Ù' => 'U', 'ũ' => 'u', 'Ũ' => 'U', 'û' => 'u', 'Û' => 'U',
'ý' => 'y', 'Ý' => 'Y', 'ž' => 'z', 'Ž' => 'Z', 'ź' => 'z', 'Ź' => 'Z'
);
return ucwords(
strtolower(
strtr(
trim($name),
$translit
)
)
);
}
/**
* Generate a license key for user's real name and expiration timestamp.
*
* $name ... real user's name
* $expires ... unix timestamp when the key expires
*/
function generate_license_key($name, $expires)
{
$name = sanitizeName($name);
$key_plain = dechex($expires) . $name;
// Get private key from a file
$fp = fopen("private-key.pem", "r");
$priv_key = fread($fp, 8192);
fclose($fp);
$res = openssl_get_privatekey($priv_key);
openssl_private_encrypt($key_plain, $crypttext, $res);
$base = base64_encode($crypttext);
return $base;
}
/**
* Veryfy if a license key is valid for the user
*/
function verify_license_key($key_base, $name)
{
$name = sanitizeName($name);
//echo "Verifying license key for $name: $key_base\n";
$key_rsa = base64_decode(trim($key_base));
$pub_key = <<<RSA
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcExAHctl38IIUw+byiMt+X7Pe
jewWPzocUQNJN/apI55J3PV0Ri2UeZBJNT8AZy4FP31xpEpXfYNlK4N8gaZ3B6jO
4XgjaSwuFvSuXc9TUlelG4doAUbxD3lNF45oZ7tUvZ5+E0g1u0udMidkCq6cPsap
kgro0rKgHXNMQjxTeQIDAQAB
-----END PUBLIC KEY-----
RSA;
$res = openssl_get_publickey($pub_key);
openssl_public_decrypt($key_rsa, $key_plain, $res);
$date = dechex(time());
$datelen = strlen($date);
if(substr($key_plain, 0, $datelen) < $date)
throw new LicenseException('Expired license key!');
$name_from_key = substr($key_plain, $datelen);
if($name_from_key != $name)
throw new LicenseException("Authorised name mismatch!");
return true;
}
Here I'm testing it:
$key = generate_license_key('Pepíček Novák', time() + 3);
echo "LICENSE KEY: $key\n";
// OK
verify_license_key($key, 'Pepíček Novák');
sleep(5);
// FAIL - EXPIRED
verify_license_key($key, 'Pepíček Novák');
ß
toß
. Also, your code will break around the end of the year 2286, because unix times will be one character longer then. – tim Oct 21 '14 at 21:00