-
-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: allow pkcs12 cert exports #470
- Loading branch information
1 parent
09fd1c9
commit 5205c7f
Showing
3 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
...g-RESTAPI/files/usr/local/pkg/RESTAPI/Endpoints/SystemCertificatePKCS12ExportEndpoint.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
namespace RESTAPI\Endpoints; | ||
|
||
require_once 'RESTAPI/autoloader.inc'; | ||
|
||
use RESTAPI\Core\Endpoint; | ||
|
||
/** | ||
* Defines an Endpoint for interacting with a singular CertificatePKCS12Export object at | ||
* /api/v2/system/certificate/pkcs12/export. | ||
*/ | ||
class SystemCertificatePKCS12ExportEndpoint extends Endpoint { | ||
public function __construct() { | ||
# Set Endpoint attributes | ||
$this->url = '/api/v2/system/certificate/pkcs12/export'; | ||
$this->model_name = 'CertificatePKCS12Export'; | ||
$this->request_method_options = ['POST']; | ||
$this->encode_content_handlers = ['BinaryContentHandler']; # Only allow binary DLs (application/octet-stream) | ||
|
||
# Set help text | ||
$this->post_help_text = 'Exports an existing certificate as a PKCS#12 archive.'; | ||
|
||
# Construct the parent Endpoint object | ||
parent::__construct(); | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/CertificatePKCS12Export.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
|
||
namespace RESTAPI\Models; | ||
|
||
use RESTAPI\Core\Model; | ||
use RESTAPI\Fields\ForeignModelField; | ||
use RESTAPI\Fields\StringField; | ||
use RESTAPI\Responses\ServerError; | ||
|
||
/** | ||
* Defines a Model for handling the export of an existing Certificate as a PKCS12 file. | ||
*/ | ||
class CertificatePKCS12Export extends Model | ||
{ | ||
public ForeignModelField $certref; | ||
public StringField $encryption; | ||
public StringField $passphrase; | ||
public StringField $filename; | ||
public StringField $binary_data; | ||
|
||
public function __construct(mixed $id = null, mixed $parent_id = null, mixed $data = [], ...$options) | ||
{ | ||
# Set Model attributes | ||
$this->internal_callable = 'get_internal'; | ||
$this->verbose_name = 'Certificate PKCS#12 Export'; | ||
|
||
# Define Model Fields | ||
$this->certref = new ForeignModelField( | ||
model_name: 'Certificate', | ||
model_field: 'refid', | ||
required: true, | ||
help_text: 'The Certificate to export as a PKCS12 file.', | ||
); | ||
$this->encryption = new StringField( | ||
default: 'high', | ||
choices: ['high', 'low', 'legacy'], | ||
help_text: 'The level of encryption to use when exporting the PKCS#12 archive.', | ||
); | ||
$this->passphrase = new StringField( | ||
default: '', | ||
allow_empty: true, | ||
help_text: 'The passphrase to use when exporting the PKCS#12 archive. Leave empty for no passphrase.', | ||
); | ||
$this->filename = new StringField( | ||
default: null, | ||
allow_null: true, | ||
read_only: true, | ||
help_text: 'The filename used when exporting the PKCS#12 archive. This value cannot be changed and will '. | ||
'always be certificate refid with the .p12 extension.', | ||
); | ||
$this->binary_data = new StringField( | ||
default: null, | ||
allow_null: true, | ||
read_only: true, | ||
help_text: 'The PKCS#12 archive binary data. This value cannot be changed.', | ||
); | ||
|
||
parent::__construct($id, $parent_id, $data, ...$options); | ||
} | ||
|
||
/** | ||
* Psuedo-method to return the internal data of the Model. This Model has no internal data but must use an internal | ||
* callable. | ||
* @return array An empty array. | ||
*/ | ||
public function get_internal(): array | ||
{ | ||
return []; | ||
} | ||
|
||
/** | ||
* Creates the PKCS#12 archive file based on the Model data. | ||
*/ | ||
public function _create(): void { | ||
# Set the filename. This will be used by the BinaryContentHandler to set the filename of the download. | ||
$this->filename->value = $this->certref->value . '.p12'; | ||
|
||
# Create the PKCS#12 archive | ||
$this->binary_data->value = cert_pkcs12_export( | ||
cert: $this->certref->get_related_model()->to_internal(), | ||
encryption: $this->encryption->value, | ||
passphrase: $this->passphrase->value, | ||
delivery: 'data' | ||
); | ||
|
||
# Throw an error if the PKCS#12 archive could not be created | ||
if (!$this->binary_data->value) { | ||
throw new ServerError( | ||
message: 'The PKCS#12 archive could not be created for unknown reasons.', | ||
response_id: 'CERTIFICATE_PKCS12_EXPORT_CREATION_FAILED', | ||
); | ||
} | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
...kg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsCertificatePKCS12ExportTestCase.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
namespace RESTAPI\Tests; | ||
|
||
use RESTAPI\Core\TestCase; | ||
use RESTAPI\Models\Certificate; | ||
use RESTAPI\Models\CertificatePKCS12Export; | ||
|
||
class APIModelsCertificatePKCS12ExportTestCase extends TestCase | ||
{ | ||
/** | ||
* Ensure the get_internal() method returns an empty array. This model has no internal representation. | ||
*/ | ||
public function test_get_internal(): void { | ||
$model = new CertificatePKCS12Export(); | ||
$this->assert_equals([], $model->get_internal()); | ||
} | ||
|
||
/** | ||
* Ensure the _create() method correctly converts the certificate to a PKCS12 archive. | ||
*/ | ||
public function test_create_with_pass(): void { | ||
# Obtain the default certificate | ||
$cert = new Certificate(id: 0); | ||
|
||
# Create a PKCS12 archive from the certificate | ||
$model = new CertificatePKCS12Export( | ||
certref: $cert->refid->value, | ||
encryption: 'high', | ||
passphrase: 'testpass', | ||
); | ||
$model->create(); | ||
|
||
# Ensure we can load the PKCS12 archive | ||
$pkcs12 = openssl_pkcs12_read($model->binary_data->value, $certs, 'testpass'); | ||
$this->assert_not_equals(false, $pkcs12); | ||
} | ||
|
||
} |