skoerfgen / ACMECert

PHP client library for Let's Encrypt and other ACME v2 - RFC 8555 compatible Certificate Authorities

Home Page:https://github.com/skoerfgen/ACMECert

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A quick question

kezenwa opened this issue · comments

How do we make this script support

Elliptic Curve Parameters

Diffie-Hellman Parameters

I believe it would be much beneficial to all users of this script.

saw an implementation but uses proc_open , proc_close , etc most of which are disabled in some hosting.

I'm not sure what you mean. Can you give an example (or show the example you saw using proc_open) what should be accomplished?

Elliptic Curve Parameters:

the generateECKey function accepts all the named curves supported by PHP but Let's Encrypt only supports P-256 (prime256v1) and P-384 (secp384r1) curves. You can get a full list by running this code:

print_r(openssl_get_curve_names());

Diffie-Hellman Parameters:

Do you mean generating DH-params like this:

openssl dhparam -out dhparams.pem 4096

?

I'm not sure what you mean. Can you give an example (or show the example you saw using proc_open) what should be accomplished?

Elliptic Curve Parameters:

the generateECKey function accepts all the named curves supported by PHP but Let's Encrypt only supports P-256 (prime256v1) and P-384 (secp384r1) curves. You can get a full list by running this code:

print_r(openssl_get_curve_names());

Diffie-Hellman Parameters:

Do you mean generating DH-params like this:

openssl dhparam -out dhparams.pem 4096

?

Thanks for the response; here's what I found but not working on some shared hostings

`
/**

  • Generate Diffie-Hellman Parameters
  • @param int $bits The length in bits
  • @return string The Diffie-Hellman Parameters as pem
    */
    public function getDhParameters(int $bits = 2048): string {
if (substr($this->dhParamFile, 0, 1) === '/') {
  $dhParamFile = $this->dhParamFile;
} else {
  $dhParamFile = $this->certAccountDir . '/' . $this->dhParamFile;
}

// If file already exists, return its content
if (file_exists($dhParamFile)) {
  $this->log('Diffie-Hellman Parameters already exists.', 'info');

  return file_get_contents($dhParamFile);
}

$ret            = 255;
$descriptorspec = [
  // stdin is a pipe that the child will read from
  0 => [
    'pipe',
    'r'
  ],
  // stdout is a pipe that the child will write to
  1 => [
    'pipe',
    'w'
  ],
  // Write progress to stdout
  2 => STDOUT
];

// Start openssl process to generate Diffie-Hellman Parameters
$this->log('Generating DH parameters, ' . (int) $bits . ' bit long safe prime, generator 2, This is going to take a long time', 'notice');
$process = proc_open('openssl dhparam -2 ' . (int) $bits . ' 2> /dev/null', $descriptorspec, $pipes);

// If process started successfully we get resource, we close input pipe and load the content of the output pipe
if (is_resource($process)) {
  fclose($pipes[0]);

  $pem = stream_get_contents($pipes[1]);
  fclose($pipes[1]);

  // It is important that you close any pipes before calling
  // proc_close in order to avoid a deadlock
  $ret = proc_close($process);
}

// On error fail
if ($ret > 0) {
  $this->log('Failed to generate Diffie-Hellman Parameters', 'exception');
  throw new \RuntimeException('Failed to generate Diffie-Hellman Parameters', 500);
}

$this->log('Diffie-Hellman Parameters generation finished.', 'notice');

// Write Parameters to file, ignore if location is not writeable
@file_put_contents($dhParamFile, $pem);

return $pem;

}

/**

  • Generate Elliptic Curve Parameters
  • @param string $curve The name of the curve
  • @return string The Diffie-Hellman Parameters as pem
    */
    public function getEcParameters(string $curve = 'prime256v1'): string {
if (substr($this->ecParamFile, 0, 1) === '/') {
  $ecParamFile = $this->ecParamFile;
} else {
  $ecParamFile = $this->certAccountDir . '/' . $this->ecParamFile;
}

// If file already exists, return its content
if (file_exists($ecParamFile)) {
  $this->log('Elliptic Curve Parameters already exists.', 'info');

  return file_get_contents($ecParamFile);
}

$ret            = 255;
$descriptorspec = [
  // stdin is a pipe that the child will read from
  0 => [
    'pipe',
    'r'
  ],
  // stdout is a pipe that the child will write to
  1 => [
    'pipe',
    'w'
  ],
  // Write progress to stdout
  2 => STDOUT
];

// Start openssl process to generate Elliptic Curve Parameters
$this->log('Start generate Elliptic Curve Parameters', 'info');
$process = proc_open('openssl ecparam -name ' . $curve, $descriptorspec, $pipes);

// If process started successfully we get resource, we close input pipe and load the content of the output pipe
if (is_resource($process)) {
  fclose($pipes[0]);

  $pem = stream_get_contents($pipes[1]);
  fclose($pipes[1]);

  // It is important that you close any pipes before calling
  // proc_close in order to avoid a deadlock
  $ret = proc_close($process);
}

// On error fail
if ($ret > 0) {
  $this->log('Failed to generate Elliptic Curve Parameters', 'exception');
  throw new \RuntimeException('Failed to generate Elliptic Curve Parameters', 500);
}

$this->log('Elliptic Curve Parameters generation finished.', 'info');

// Write Parameters to file, ignore if location is not writeable
@file_put_contents($ecParamFile, $pem);

return $pem;

}
`

The "Elliptic Curve Parameters"-example generates only the curve name, for example if you run the following code:

echo getEcParameters('prime256v1');

it outputs:

-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----

which only contains the curve name: (you can decode it using: openssl ecparam -noout -text):

ASN1 OID: prime256v1
NIST CURVE: P-256

The generateECKey function on the other hand creates an EC private key using the specified curve name, for example:

echo $ac->generateECKey('prime256v1');

(which is equivalent to using openssl ecparam -name prime256v1 -genkey -noout)

it outputs something like:

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIGktYNIo8IQr5wXRV5YlfKv2wXdsrHA/DziQXTdBEEYgoAoGCCqGSM49
AwEHoUQDQgAEcGVF1wscO6rx0ksgNXgWnWLvWqYetcdU8J3meRF4VY9sjMvsixFo
3rAHN9VGrXBucij6lai+1k2ChROV8RcD3A==
-----END EC PRIVATE KEY-----

which is an EC key based on the curve 'prime256v1', which can be used as account key or private key for a certificate.

Unfortunately the "Diffie-Hellman Parameters"-example can't be implemented using PHP only without using something like proc_open or similar (there is no function for it in the OpenSSL extension)

Diffie-Hellma

in other words, the function getEcParameters() isn't necessarily useful since we already have the method generateECKey() which does much more.

As for the getDhParameters() , You agree it's useful though can't be generated or rewritten in pure simple PHP without using functions which often are disabled in some hosting Servers.

Okay now I have written a few conditions which runs getDhParameters() only when the necessary needed functions are available.

What am yet to figure out is how to apply it's output. From my little observation, it produces similar output as the generateECKey() and generateRSAKey but how do I use it's output ?

Thanks in advance

Hello

Am in here again, sorry if you feel disturbed BUT i really need a portion of your wealth of experience just a little.

Have got an idea (kinda of though untested yet) on how to totally almost avoid hitting Rate Limits BUT I need know at what point exactly such requests are made which after a couple of times, could hit such limits.

For instance, what url end-point is visited.

What am looking to do is, if I know what method is run and what url end-point the request is sent to, I could embed my function / idea along it's path so my function / idea makes the decision of deciding on where such requests should be done or not.

Am trying to guess the method private $this->register() which I have recreated as a protected method $this->_register() is what I should be looking for BUT i still would be much confident and sure hearing it from someone much experienced.

Thanks in Advance

in other words, the function getEcParameters() isn't necessarily useful since we already have the method generateECKey() which does much more.

As for the getDhParameters() , You agree it's useful though can't be generated or rewritten in pure simple PHP without using functions which often are disabled in some hosting Servers.

It may be useful in general, but not in the context of ACMECert directly, since it is all about getting a certificate but not configuring the web server itself.

What am yet to figure out is how to apply it's output. From my little observation, it produces similar output as the generateECKey() and generateRSAKey but how do I use it's output ?

The output of getDhParameters() can be set in the webserver configuration to use these custom DH parameters for the key exchange.

For nginx you can use ssl_dhparam
For apache:

Custom DH parameters and an EC curve name for ephemeral keys, can also be added to end of the first file configured using SSLCertificateFile. This is supported in version 2.4.7 or later. Such parameters can be generated using the commands openssl dhparam and openssl ecparam. The parameters can be added as-is to the end of the first certificate file. Only the first file can be used for custom parameters, as they are applied independently of the authentication algorithm type.
(source: https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatefile)

Just append the DH parameters to the existing certificate file. The resulting file should look like this:

-----BEGIN CERTIFICATE-----
stuff
-----END CERTIFICATE-----
-----BEGIN DH PARAMETERS-----
more stuff
-----END DH PARAMETERS-----

Or when using apache>=2.4.8 (and openssl>=1.0.2) you can also use something like:

SSLOpenSSLConfCmd DHParameters "/etc/ssl/certs/dhparam.pem"

also see: https://security.stackexchange.com/q/94390

Hello

Am in here again, sorry if you feel disturbed BUT i really need a portion of your wealth of experience just a little.

Have got an idea (kinda of though untested yet) on how to totally almost avoid hitting Rate Limits BUT I need know at what point exactly such requests are made which after a couple of times, could hit such limits.

For instance, what url end-point is visited.

What am looking to do is, if I know what method is run and what url end-point the request is sent to, I could embed my function / idea along it's path so my function / idea makes the decision of deciding on where such requests should be done or not.

Am trying to guess the method private $this->register() which I have recreated as a protected method $this->_register() is what I should be looking for BUT i still would be much confident and sure hearing it from someone much experienced.

Thanks in Advance

When you run ACMECert, it outputs each visited url end-point.

It is not possible to tell if a rate limit is reached by only looking at the visited urls/invoked methods, since it depends on other factors as well. For example when creating a new order https://acme-v02.api.letsencrypt.org/acme/new-order it depends also on which domain names are requested ("Certificates per Registered Domain"-limit) and whether it is a renewal ("Duplicate Certificate"-limit) or not. Also it depends if the validation failed ("Failed Validation"-limit) or not.

The register function you mentioned would count against the "Accounts per IP Address"-limit.

There are also different rate limits for the live and staging environment.

When using:

$ac=new ACMECert();

the live environment is used: https://acme-v02.api.letsencrypt.org/

rate limits for live environment: https://letsencrypt.org/docs/rate-limits/

or when using:

$ac=new ACMECert(false);

the staging environment is used: https://acme-staging-v02.api.letsencrypt.org/

rate limits for staging environment: https://letsencrypt.org/docs/staging-environment/#rate-limits

So there is no easy way to dertermine whether a rate limit is reached without considering a lot of factors.

I am moving this conversation to the discussion section, since it is not really an issue concerning ACMECert.