How to create new account with ECDSA key

Hi, I develop the client that are compatible with ACME v2.

I tried to create new account with ECDSA key, but run into an problem.
Can you please teach me the right way to create new account with ECDSA key?

In case of RSA key, I succeeded in creation new account.

<?php

namespace {
    require_once __DIR__ . '/../vendor/autoload.php';

    $resource = openssl_pkey_new([
        'private_key_type' => OPENSSL_KEYTYPE_RSA,
        'private_key_bits' => 2048,
    ]);

    openssl_pkey_export($resource, $privkey);
    $details = openssl_pkey_get_details($resource);
    openssl_free_key($resource);

    $guzzle   = new \GuzzleHttp\Client();
    $response = $guzzle->head('https://acme-staging-v02.api.letsencrypt.org/directory');
    $nonce    = $response->getHeaderLine('Replay-Nonce');

    $header = base64_url_safe_encode(json_encode([
            'alg' => 'RS256',
            'jwk' => [
                'kty' => 'RSA',
                'n'   => base64_url_safe_encode($details['rsa']['n']),
                'e'   => base64_url_safe_encode($details['rsa']['e']),
            ],
            'nonce' => $nonce,
            'url'   => 'https://acme-staging-v02.api.letsencrypt.org/acme/new-acct',
    ]));

    $payload = base64_url_safe_encode(json_encode([
        'contact' => [],
        'termsOfServiceAgreed' => true,
    ]));

    openssl_sign($header . '.' . $payload, $signature, $privkey, 'sha256');

    $jws = json_encode([
        'protected' => $header,
        'payload'   => $payload,
        'signature' => base64_url_safe_encode($signature),
    ]);

    $response = $guzzle->post('https://acme-staging-v02.api.letsencrypt.org/acme/new-acct', [
        'headers' => [
            'Accept'       => 'application/jose+json',
            'Content-Type' => 'application/jose+json',
        ],
        'body' => $jws,
    ]);

    var_dump((string)$response->getBody());
}

In case of ECDSA key, I failed to create new account.

<?php

namespace {
    require_once __DIR__ . '/../vendor/autoload.php';

    $resource = openssl_pkey_new([
        'private_key_type' => OPENSSL_KEYTYPE_EC,
        'curve_name' => 'prime256v1',
    ]);

    openssl_pkey_export($resource, $privkey);
    $details = openssl_pkey_get_details($resource);
    openssl_free_key($resource);

    $guzzle   = new \GuzzleHttp\Client();
    $response = $guzzle->head('https://acme-staging-v02.api.letsencrypt.org/directory');
    $nonce    = $response->getHeaderLine('Replay-Nonce');

    $header = base64_url_safe_encode(json_encode([
        'alg' => 'ES256',
        'jwk' => [
            'kty' => 'EC',
            'crv' => 'P-256',
            'x'   => base64_url_safe_encode($details['ec']['x']),
            'y'   => base64_url_safe_encode($details['ec']['y']),
        ],
        'nonce' => $nonce,
        'url'   => 'https://acme-staging-v02.api.letsencrypt.org/acme/new-acct',
    ]));

    $payload = base64_url_safe_encode(json_encode([
        'contact' => [],
        'termsOfServiceAgreed' => true,
    ]));

    openssl_sign($header . '.' . $payload, $signature, $privkey, 'sha256');

    $jws = json_encode([
        'protected' => $header,
        'payload'   => $payload,
        'signature' => base64_url_safe_encode($signature),
    ]);

    $response = $guzzle->post('https://acme-staging-v02.api.letsencrypt.org/acme/new-acct', [
        'headers' => [
            'Accept'       => 'application/jose+json',
            'Content-Type' => 'application/jose+json',
        ],
        'body' => $jws,
    ]);

    var_dump((string)$response->getBody());
}

Thanks.

What was the error your received when creating the EC based account?

1 Like

I’m not even sure you can have an LE account that is type EC.
All my ECC certs (and RSA certs) are using RSA accounts.
I gave up trying that long ago.
But I think, if I recall correctly, it had something to do with LE root cert/HSM only doing RSA.
[total fuzzy guessing]

I'm sure :slight_smile: You definitely can.

2 Likes

2 posts were split to a new topic: Does Lets Encrypt support EC ACME account keys?

So I think your problem is the use of openssl_sign. I believe this is returning a ASN.1/DER-encoded ECDSA signature, which is variable-length, upto 72 bytes for P-256+SHA256.

You need to produce the fixed-length representation of the signature (64 bytes).

So you need to extract the two integers from the ECDSA signature and re-encode them. I found a single PHP ACME client that does this.

Funnily enough, most other PHP ACME clients that I checked avoid implementing EC support for account keys, probably exactly because of this complication.

Another problem I noticed is that you have kty ordered before crv in your JWK, off the top of my head, crv needs to appear first (lexicographical ordering).

4 Likes

Thank you very much, everyone!
The problem was solved completely.

As @_az said, The problem is the use of openssl_sign .
I should have dumped ASN.1 object that was generated by openssl_sign .

Now, I succeeded in creation new account with ECDSA key.
Thanks!

3 Likes

6 posts were split to a new topic: Help creating new account/JWS errors

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.