/** * Generates a certificate with Let's Encrypt. * * @since 1.0.0 * @access public * * @param string $domain The root domain to generate the certificate for. * @param array $addon_domains Additional domains to also include in the certificate. * @param array $dn_args Array of CSR settings. It should have the array keys * 'ST' (for country), 'C' (for two-letter country code) * and 'O' (for organization name). * @return array|WP_Error The response if successful, an error object otherwise. */ public function generate_certificate($domain, $addon_domains = array(), $dn_args = array()) { $account_keypair = AccountKeyPair::get(); $account_key_details = $account_keypair->get_private_details(); if (is_wp_error($account_key_details)) { return $account_key_details; } $all_domains = Util::get_all_domains($domain, $addon_domains); foreach ($all_domains as $_domain) { $status = Challenge::validate($_domain, $account_key_details); if (is_wp_error($status)) { return $status; } } $domain_keypair = DomainKeyPair::get($domain); if (!$domain_keypair->exists()) { $status = $domain_keypair->generate(); if (is_wp_error($status)) { return $status; } } $domain_key_resource = $domain_keypair->read_private(); if (is_wp_error($domain_key_resource)) { return $domain_key_resource; } $certificate = Certificate::get($domain); $csr = $certificate->generate_csr($domain_key_resource, $all_domains, $dn_args); if (is_wp_error($csr)) { return $csr; } $client = Client::get(); $result = $client->generate($csr); if (is_wp_error($result)) { return $result; } if (201 !== $client->get_last_code()) { return $this->parse_wp_error($result, 'new_cert_invalid_response_code', __('Invalid response code for new certificate request.', 'wp-encrypt')); } $location = $client->get_last_location(); $certs = array(); $done = false; do { $result = $client->request($location, 'GET'); if (is_wp_error($result)) { return $result; } if (202 === $client->get_last_code()) { sleep(1); } elseif (200 === $client->get_last_code()) { $certs[] = $result; foreach ($client->get_last_links() as $link) { $result = $client->request($link, 'GET'); if (is_wp_error($result)) { return $result; } $certs[] = $result; } $done = true; } else { return $this->parse_wp_error($result, 'new_cert_invalid_response_code', __('Invalid response code for new certificate request.', 'wp-encrypt')); } } while (!$done); if (0 === count($certs)) { return new WP_Error('new_cert_fail', __('No certificates generated.', 'wp-encrypt')); } $status = $certificate->set($certs); if (is_wp_error($status)) { return $status; } return array('domains' => $all_domains); }
/** * Sends a signed request to the Let's Encrypt API. * * All requests except the directory request go through this method. * * @since 1.0.0 * @access public * * @param string $endpoint The endpoint to send a request to. * @param array $data Data to send with the request. * @return string|array|WP_Error Either a JSON-decoded array response, a plain text response or an error object. */ public function signed_request($endpoint, $data = null) { $account_keypair = AccountKeyPair::get(); $account_key_resource = $account_keypair->read_private(); if (is_wp_error($account_key_resource)) { return $account_key_resource; } $account_key_details = $account_keypair->get_private_details(); if (is_wp_error($account_key_details)) { return $account_key_details; } $protected = $header = array('alg' => 'RS256', 'jwk' => array('kty' => 'RSA', 'n' => Util::base64_url_encode($account_key_details['rsa']['n']), 'e' => Util::base64_url_encode($account_key_details['rsa']['e']))); if (null !== ($nonce = $this->get_last_nonce())) { $protected['nonce'] = $nonce; } else { $this->directory(); if (null !== ($nonce = $this->get_last_nonce())) { $protected['nonce'] = $nonce; } } if (!isset($protected['nonce'])) { return new WP_Error('signed_request_no_nonce', __('No nonce available for a signed request.', 'wp-encrypt')); } $data64 = Util::base64_url_encode(str_replace('\\/', '/', json_encode($data))); $protected64 = Util::base64_url_encode(json_encode($protected)); $sign_status = openssl_sign($protected64 . '.' . $data64, $signature, $account_key_resource, 'SHA256'); if (false === $sign_status) { return new WP_Error('private_key_cannot_sign', sprintf(__('Could not sign request with private key. Original error message: %s', 'wp-encrypt'), openssl_error_string())); } $signature64 = Util::base64_url_encode($signature); return $this->request($endpoint, 'POST', array('header' => $header, 'protected' => $protected64, 'payload' => $data64, 'signature' => $signature64)); }