diff --git a/performance/KeyFactory/KeyFactory.php b/performance/KeyFactory/KeyFactory.php new file mode 100644 index 00000000..34c21f32 --- /dev/null +++ b/performance/KeyFactory/KeyFactory.php @@ -0,0 +1,69 @@ +createPrivateKey(); + $publicKey = $curve->createPublicKey($privateKey); + + JWK::create([ + 'kty' => 'EC', + 'crv' => $curve, + 'd' => Base64Url::encode(gmp_export($privateKey->getSecret())), + 'x' => Base64Url::encode(gmp_export($publicKey->getPoint()->getX())), + 'y' => Base64Url::encode(gmp_export($publicKey->getPoint()->getY())), + ]); + } + + /** + * @Subject() + */ + public function usingOpenSSL() + { + $key = openssl_pkey_new([ + 'curve_name' => 'prime256v1', + 'private_key_type' => OPENSSL_KEYTYPE_EC, + ]); + $res = openssl_pkey_export($key, $out); + if (false === $res) { + throw new \RuntimeException('Unable to create the key'); + } + $res = openssl_pkey_get_private($out); + + $details = openssl_pkey_get_details($res); + + JWK::create([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => Base64Url::encode($details['ec']['x']), + 'y' => Base64Url::encode($details['ec']['y']), + 'd' => Base64Url::encode($details['ec']['d']), + ]); + } +} diff --git a/src/Component/Core/Util/Ecc/NistCurve.php b/src/Component/Core/Util/Ecc/NistCurve.php index 5ebab792..2a67ee64 100644 --- a/src/Component/Core/Util/Ecc/NistCurve.php +++ b/src/Component/Core/Util/Ecc/NistCurve.php @@ -49,12 +49,12 @@ class NistCurve */ public static function curve256(): Curve { - $p = gmp_init('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff', 16); - $a = gmp_init('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc', 16); - $b = gmp_init('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16); - $x = gmp_init('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296', 16); - $y = gmp_init('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5', 16); - $n = gmp_init('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551', 16); + $p = gmp_init('ffffffff00000001000000000000000000000000ffffffffffffffffffffffff', 16); + $a = gmp_init('ffffffff00000001000000000000000000000000fffffffffffffffffffffffc', 16); + $b = gmp_init('5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16); + $x = gmp_init('6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296', 16); + $y = gmp_init('4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5', 16); + $n = gmp_init('ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551', 16); $generator = Point::create($x, $y, $n); return new Curve(256, $p, $a, $b, $generator); @@ -67,12 +67,12 @@ public static function curve256(): Curve */ public static function curve384(): Curve { - $p = gmp_init('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff', 16); - $a = gmp_init('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc', 16); - $b = gmp_init('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16); - $x = gmp_init('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7', 16); - $y = gmp_init('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f', 16); - $n = gmp_init('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973', 16); + $p = gmp_init('fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff', 16); + $a = gmp_init('fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc', 16); + $b = gmp_init('b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16); + $x = gmp_init('aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7', 16); + $y = gmp_init('3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f', 16); + $n = gmp_init('ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973', 16); $generator = Point::create($x, $y, $n); return new Curve(384, $p, $a, $b, $generator); @@ -85,12 +85,12 @@ public static function curve384(): Curve */ public static function curve521(): Curve { - $p = gmp_init('0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16); - $a = gmp_init('0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc', 16); - $b = gmp_init('0x00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', 16); - $x = gmp_init('0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66', 16); - $y = gmp_init('0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650', 16); - $n = gmp_init('0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409', 16); + $p = gmp_init('000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16); + $a = gmp_init('000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc', 16); + $b = gmp_init('00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', 16); + $x = gmp_init('000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66', 16); + $y = gmp_init('0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650', 16); + $n = gmp_init('000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409', 16); $generator = Point::create($x, $y, $n); return new Curve(521, $p, $a, $b, $generator); diff --git a/src/Component/Encryption/Algorithm/KeyEncryption/ECDHES.php b/src/Component/Encryption/Algorithm/KeyEncryption/ECDHES.php index 8af0bd52..eddcaa3b 100644 --- a/src/Component/Encryption/Algorithm/KeyEncryption/ECDHES.php +++ b/src/Component/Encryption/Algorithm/KeyEncryption/ECDHES.php @@ -264,17 +264,13 @@ private function convertDecToBin(\GMP $dec): string */ public function createECKey(string $crv): JWK { - $curve = $this->getCurve($crv); - $privateKey = $curve->createPrivateKey(); - $point = $curve->createPublicKey($privateKey)->getPoint(); + try { + $jwk = self::createECKeyUsingOpenSSL($crv); + } catch (\Exception $e) { + $jwk = self::createECKeyUsingPurePhp($crv); + } - return JWK::create([ - 'kty' => 'EC', - 'crv' => $crv, - 'x' => Base64Url::encode($this->convertDecToBin($point->getX())), - 'y' => Base64Url::encode($this->convertDecToBin($point->getY())), - 'd' => Base64Url::encode($this->convertDecToBin($privateKey->getSecret())), - ]); + return JWK::create($jwk); } /** @@ -308,4 +304,87 @@ public static function createOKPKey(string $curve): JWK 'd' => Base64Url::encode($d), ]); } + + /** + * @param string $curve + * + * @return array + */ + private static function createECKeyUsingPurePhp(string $curve): array + { + switch ($curve) { + case 'P-256': + $nistCurve = NistCurve::curve256(); + + break; + case 'P-384': + $nistCurve = NistCurve::curve384(); + + break; + case 'P-521': + $nistCurve = NistCurve::curve521(); + + break; + default: + throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)); + } + + $privateKey = $nistCurve->createPrivateKey(); + $publicKey = $nistCurve->createPublicKey($privateKey); + + return [ + 'kty' => 'EC', + 'crv' => $curve, + 'd' => Base64Url::encode(gmp_export($privateKey->getSecret())), + 'x' => Base64Url::encode(gmp_export($publicKey->getPoint()->getX())), + 'y' => Base64Url::encode(gmp_export($publicKey->getPoint()->getY())), + ]; + } + + /** + * @param string $curve + * + * @return array + */ + private static function createECKeyUsingOpenSSL(string $curve): array + { + $key = openssl_pkey_new([ + 'curve_name' => self::getOpensslCurveName($curve), + 'private_key_type' => OPENSSL_KEYTYPE_EC, + ]); + $res = openssl_pkey_export($key, $out); + if (false === $res) { + throw new \RuntimeException('Unable to create the key'); + } + $res = openssl_pkey_get_private($out); + + $details = openssl_pkey_get_details($res); + + return [ + 'kty' => 'EC', + 'crv' => $curve, + 'd' => Base64Url::encode($details['ec']['d']), + 'x' => Base64Url::encode($details['ec']['x']), + 'y' => Base64Url::encode($details['ec']['y']), + ]; + } + + /** + * @param string $curve + * + * @return string + */ + private static function getOpensslCurveName(string $curve): string + { + switch ($curve) { + case 'P-256': + return 'prime256v1'; + case 'P-384': + return 'secp384r1'; + case 'P-521': + return 'secp521r1'; + default: + throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)); + } + } } diff --git a/src/Component/KeyManagement/JWKFactory.php b/src/Component/KeyManagement/JWKFactory.php index 1e27a0dc..e2040749 100644 --- a/src/Component/KeyManagement/JWKFactory.php +++ b/src/Component/KeyManagement/JWKFactory.php @@ -63,6 +63,23 @@ public static function createRSAKey(int $size, array $values = []): JWK * @return JWK */ public static function createECKey(string $curve, array $values = []): JWK + { + try { + $jwk = self::createECKeyUsingOpenSSL($curve); + } catch (\Exception $e) { + $jwk = self::createECKeyUsingPurePhp($curve); + } + $values = array_merge($values, $jwk); + + return JWK::create($values); + } + + /** + * @param string $curve + * + * @return array + */ + private static function createECKeyUsingPurePhp(string $curve): array { switch ($curve) { case 'P-256': @@ -84,18 +101,60 @@ public static function createECKey(string $curve, array $values = []): JWK $privateKey = $nistCurve->createPrivateKey(); $publicKey = $nistCurve->createPublicKey($privateKey); - $values = array_merge( - $values, - [ - 'kty' => 'EC', - 'crv' => $curve, - 'd' => Base64Url::encode(gmp_export($privateKey->getSecret())), - 'x' => Base64Url::encode(gmp_export($publicKey->getPoint()->getX())), - 'y' => Base64Url::encode(gmp_export($publicKey->getPoint()->getY())), - ] - ); + return [ + 'kty' => 'EC', + 'crv' => $curve, + 'd' => Base64Url::encode(gmp_export($privateKey->getSecret())), + 'x' => Base64Url::encode(gmp_export($publicKey->getPoint()->getX())), + 'y' => Base64Url::encode(gmp_export($publicKey->getPoint()->getY())), + ]; + } - return JWK::create($values); + /** + * @param string $curve + * + * @return array + */ + private static function createECKeyUsingOpenSSL(string $curve): array + { + $key = openssl_pkey_new([ + 'curve_name' => self::getOpensslCurveName($curve), + 'private_key_type' => OPENSSL_KEYTYPE_EC, + ]); + $res = openssl_pkey_export($key, $out); + if (false === $res) { + throw new \RuntimeException('Unable to create the key'); + } + $res = openssl_pkey_get_private($out); + + $details = openssl_pkey_get_details($res); + + return [ + 'kty' => 'EC', + 'crv' => $curve, + 'x' => Base64Url::encode(bin2hex($details['ec']['x'])), + 'y' => Base64Url::encode(bin2hex($details['ec']['y'])), + 'd' => Base64Url::encode(bin2hex($details['ec']['d'])), + ]; + } + + /** + * @param string $curve + * + * @return string + */ + private static function getOpensslCurveName(string $curve): string + { + switch ($curve) { + case 'P-256': + return 'prime256v1'; + case 'P-384': + return 'secp384r1'; + case 'P-521': + return 'secp521r1'; + default: + throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)); + } } /**