/** * @param $base64 * @return array */ public function unserialize($base64) { $binary = base64_decode($base64, true); if ($binary === false) { throw new \InvalidArgumentException('Invalid base64'); } $values = []; $pos = 0; $end = strlen($binary); for ($i = 0; $i < 3; $i++) { if ($end - $pos < 4) { throw new \RuntimeException('Length marker too short'); } $length = unpack("N", substr($binary, $pos, 4))[1]; $pos += 4; if ($end - $pos < $length) { throw new \RuntimeException('Not enough data'); } $value = substr($binary, $pos, $length); $pos += $length; $values[$i] = $value; } $curveName = $values[1]; $pointHex = unpack("H*", $values[2])[1]; $curve = Curves::curve($curveName); $generator = Curves::generator($curveName); $point = $this->pointSerializer->unserialize($curve, $pointHex); $publicKey = new PublicKey($this->math, $generator, $point); return [$curve, $publicKey]; }
function setVAPIDInfo($privateKey, $audience, $subject) { if (!USE_VAPID || !$privateKey || !$audience || !$subject) { return; } $builder = new Builder(); $token = $builder->setAudience($audience)->setExpiration(time() + 86400)->setSubject($subject)->sign(new Sha256(), new Key($privateKey))->getToken(); $this->additionalHeaders['Authorization'] = 'Bearer ' . $token; $privKeySerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer()); $privateKeyObject = $privKeySerializer->parse($privateKey); $publicKeyObject = $privateKeyObject->getPublicKey(); $pointSerializer = new UncompressedPointSerializer(EccFactory::getAdapter()); $this->additionalHeaders['Crypto-Key'] = 'p256ecdsa=' . Base64Url::encode(hex2bin($pointSerializer->serialize($publicKeyObject->getPoint()))); }
function get_public_key($privateKey) { $publicKeyVal = __('Your private key is invalid.', 'web-push'); error_reporting(E_ERROR); try { $privKeySerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer()); $privateKeyObject = $privKeySerializer->parse($privateKey); $publicKeyObject = $privateKeyObject->getPublicKey(); $pointSerializer = new UncompressedPointSerializer(EccFactory::getAdapter()); $publicKeyVal = Base64Url::encode(hex2bin($pointSerializer->serialize($publicKeyObject->getPoint()))); } catch (Exception $e) { // Ignore exceptions while getting the public key from the private key. } error_reporting(E_ALL); return $publicKeyVal; }
/** * @param string $payload With padding * @param string $userPublicKey Base 64 encoded (MIME or URL-safe) * @param string $userAuthToken Base 64 encoded (MIME or URL-safe) * @param bool $nativeEncryption Use OpenSSL (>PHP7.1) * * @return array */ public static function encrypt($payload, $userPublicKey, $userAuthToken, $nativeEncryption) { $userPublicKey = Base64Url::decode($userPublicKey); $userAuthToken = Base64Url::decode($userAuthToken); // initialize utilities $math = EccFactory::getAdapter(); $pointSerializer = new UncompressedPointSerializer($math); $generator = EccFactory::getNistCurves()->generator256(); $curve = EccFactory::getNistCurves()->curve256(); // get local key pair $localPrivateKeyObject = $generator->createPrivateKey(); $localPublicKeyObject = $localPrivateKeyObject->getPublicKey(); $localPublicKey = hex2bin($pointSerializer->serialize($localPublicKeyObject->getPoint())); // get user public key object $pointUserPublicKey = $pointSerializer->unserialize($curve, bin2hex($userPublicKey)); $userPublicKeyObject = $generator->getPublicKeyFrom($pointUserPublicKey->getX(), $pointUserPublicKey->getY(), $generator->getOrder()); // get shared secret from user public key and local private key $sharedSecret = hex2bin($math->decHex(gmp_strval($userPublicKeyObject->getPoint()->mul($localPrivateKeyObject->getSecret())->getX()))); // generate salt $salt = openssl_random_pseudo_bytes(16); // section 4.3 $ikm = !empty($userAuthToken) ? self::hkdf($userAuthToken, $sharedSecret, 'Content-Encoding: auth' . chr(0), 32) : $sharedSecret; // section 4.2 $context = self::createContext($userPublicKey, $localPublicKey); // derive the Content Encryption Key $contentEncryptionKeyInfo = self::createInfo('aesgcm', $context); $contentEncryptionKey = self::hkdf($salt, $ikm, $contentEncryptionKeyInfo, 16); // section 3.3, derive the nonce $nonceInfo = self::createInfo('nonce', $context); $nonce = self::hkdf($salt, $ikm, $nonceInfo, 12); // encrypt // "The additional data passed to each invocation of AEAD_AES_128_GCM is a zero-length octet sequence." if (!$nativeEncryption) { list($encryptedText, $tag) = \AESGCM\AESGCM::encrypt($contentEncryptionKey, $nonce, $payload, ''); } else { $encryptedText = openssl_encrypt($payload, 'aes-128-gcm', $contentEncryptionKey, OPENSSL_RAW_DATA, $nonce, $tag); // base 64 encoded } // return values in url safe base64 return array('localPublicKey' => Base64Url::encode($localPublicKey), 'salt' => Base64Url::encode($salt), 'cipherText' => $encryptedText . $tag); }
private static function getUncompressedKeys(PrivateKeyInterface $privateKeyObject) { $pointSerializer = new UncompressedPointSerializer(EccFactory::getAdapter()); $vapid['publicKey'] = base64_encode(hex2bin($pointSerializer->serialize($privateKeyObject->getPublicKey()->getPoint()))); $vapid['privateKey'] = base64_encode(hex2bin(str_pad(gmp_strval($privateKeyObject->getSecret(), 16), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT))); return $vapid; }