OPENSSL_KEYTYPE_EC, 'curve_name' => 'secp521r1', ]); if ($privateKey === false) { throw new \RuntimeException('Failed to generate private key: '.openssl_error_string()); } if (! openssl_pkey_export($privateKey, $privateKeyStr)) { throw new \RuntimeException('Failed to export private key: '.openssl_error_string()); } if (! is_null($serverId) && ! $isCaCertificate) { $server = Server::find($serverId); if ($server) { $ip = $server->getIp; if ($ip) { $type = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) ? 'IP' : 'DNS'; $subjectAlternativeNames = array_unique( array_merge($subjectAlternativeNames, ["$type:$ip"]) ); } } } $basicConstraints = $isCaCertificate ? 'critical, CA:TRUE, pathlen:0' : 'critical, CA:FALSE'; $keyUsage = $isCaCertificate ? 'critical, keyCertSign, cRLSign' : 'critical, digitalSignature, keyAgreement'; $subjectAltNameSection = ''; $extendedKeyUsageSection = ''; if (! $isCaCertificate) { $extendedKeyUsageSection = "\nextendedKeyUsage = serverAuth, clientAuth"; $subjectAlternativeNames = array_values( array_unique( array_merge(["DNS:$commonName"], $subjectAlternativeNames) ) ); $formattedSubjectAltNames = array_map( function ($index, $san) { [$type, $value] = explode(':', $san, 2); return "{$type}.".($index + 1)." = $value"; }, array_keys($subjectAlternativeNames), $subjectAlternativeNames ); $subjectAltNameSection = "subjectAltName = @subject_alt_names\n\n[ subject_alt_names ]\n" .implode("\n", $formattedSubjectAltNames); } $config = << $commonName, 'organizationName' => $organizationName, 'countryName' => $countryName, 'stateOrProvinceName' => $stateName, ], $privateKey, [ 'digest_alg' => 'sha512', 'config' => $tempConfigPath, 'req_extensions' => 'req_ext', ]); if ($csr === false) { throw new \RuntimeException('Failed to generate CSR: '.openssl_error_string()); } $certificate = openssl_csr_sign( $csr, $caCert ?? null, $caKey ?? $privateKey, $validityDays, [ 'digest_alg' => 'sha512', 'config' => $tempConfigPath, 'x509_extensions' => 'v3_req', ], random_int(1, PHP_INT_MAX) ); if ($certificate === false) { throw new \RuntimeException('Failed to sign certificate: '.openssl_error_string()); } if (! openssl_x509_export($certificate, $certificateStr)) { throw new \RuntimeException('Failed to export certificate: '.openssl_error_string()); } SslCertificate::query() ->where('resource_type', $resourceType) ->where('resource_id', $resourceId) ->where('server_id', $serverId) ->delete(); $sslCertificate = SslCertificate::create([ 'ssl_certificate' => $certificateStr, 'ssl_private_key' => $privateKeyStr, 'resource_type' => $resourceType, 'resource_id' => $resourceId, 'server_id' => $serverId, 'configuration_dir' => $configurationDir, 'mount_path' => $mountPath, 'valid_until' => CarbonImmutable::now()->addDays($validityDays), 'is_ca_certificate' => $isCaCertificate, 'common_name' => $commonName, 'subject_alternative_names' => $subjectAlternativeNames, ]); if ($configurationDir && $mountPath && $resourceType && $resourceId) { $model = app($resourceType)->find($resourceId); $model->fileStorages() ->where('resource_type', $model->getMorphClass()) ->where('resource_id', $model->id) ->get() ->filter(function ($storage) use ($mountPath) { return in_array($storage->mount_path, [ $mountPath.'/server.crt', $mountPath.'/server.key', $mountPath.'/server.pem', ]); }) ->each(function ($storage) { $storage->delete(); }); if ($isPemKeyFileRequired) { $model->fileStorages()->create([ 'fs_path' => $configurationDir.'/ssl/server.pem', 'mount_path' => $mountPath.'/server.pem', 'content' => $certificateStr."\n".$privateKeyStr, 'is_directory' => false, 'chmod' => '600', 'resource_type' => $resourceType, 'resource_id' => $resourceId, ]); } else { $model->fileStorages()->create([ 'fs_path' => $configurationDir.'/ssl/server.crt', 'mount_path' => $mountPath.'/server.crt', 'content' => $certificateStr, 'is_directory' => false, 'chmod' => '644', 'resource_type' => $resourceType, 'resource_id' => $resourceId, ]); $model->fileStorages()->create([ 'fs_path' => $configurationDir.'/ssl/server.key', 'mount_path' => $mountPath.'/server.key', 'content' => $privateKeyStr, 'is_directory' => false, 'chmod' => '600', 'resource_type' => $resourceType, 'resource_id' => $resourceId, ]); } } return $sslCertificate; } catch (\Throwable $e) { throw new \RuntimeException('SSL Certificate generation failed: '.$e->getMessage(), 0, $e); } finally { fclose($tempConfig); } } }