+=method save
+
+ $key->save;
+ $key->save(%options);
+
+Write a key file. Available options:
+
+=for :list
+* C<type> - Type of key file (default: value of L</type>, or C<KEY_FILE_TYPE_XML>)
+* C<verson> - Version of key file (default: value of L</version>, or 2)
+* C<filepath> - Where to save the file (default: value of L</filepath>)
+* C<fh> - IO handle to write to (overrides C<filepath>, one of which must be defined)
+* C<raw_key> - Raw key (default: value of L</raw_key>)
+
+=cut
+
+sub save {
+ my $self = shift;
+ my %args = @_;
+
+ my @cleanup;
+ my $raw_key = $args{raw_key} // $self->raw_key // random_bytes(32);
+ push @cleanup, erase_scoped $raw_key;
+ length($raw_key) == 32 or throw 'Raw key must be exactly 256 bits (32 bytes)', length => length($raw_key);
+
+ my $type = $args{type} // $self->type // KEY_FILE_TYPE_XML;
+ my $version = $args{version} // $self->version // 2;
+ my $filepath = $args{filepath} // $self->filepath;
+ my $fh = $args{fh};
+
+ my $filepath_temp;
+ if (!openhandle($fh)) {
+ $filepath or throw 'Must specify where to safe the key file to';
+
+ require File::Temp;
+ ($fh, $filepath_temp) = eval { File::Temp::tempfile("${filepath}-XXXXXX", CLEANUP => 1) };
+ if (!$fh or my $err = $@) {
+ $err //= 'Unknown error';
+ throw sprintf('Open file failed (%s): %s', $filepath_temp, $err),
+ error => $err,
+ filepath => $filepath_temp;
+ }
+ }
+
+ if ($type == KEY_FILE_TYPE_XML) {
+ $self->_save_xml($fh, $raw_key, $version);
+ }
+ elsif ($type == KEY_FILE_TYPE_BINARY) {
+ print $fh $raw_key;
+ }
+ elsif ($type == KEY_FILE_TYPE_HEX) {
+ my $hex = uc(unpack('H*', $raw_key));
+ push @cleanup, erase_scoped $hex;
+ print $fh $hex;
+ }
+ else {
+ throw "Cannot save $type key file (invalid type)", type => $type;
+ }
+
+ close($fh);
+
+ if ($filepath_temp) {
+ my ($file_mode, $file_uid, $file_gid) = (stat($filepath))[2, 4, 5];
+
+ my $mode = $args{mode} // $file_mode // do { my $m = umask; defined $m ? oct(666) &~ $m : undef };
+ my $uid = $args{uid} // $file_uid // -1;
+ my $gid = $args{gid} // $file_gid // -1;
+ chmod($mode, $filepath_temp) if defined $mode;
+ chown($uid, $gid, $filepath_temp);
+ rename($filepath_temp, $filepath)
+ or throw "Failed to write file ($filepath): $!", filepath => $filepath;
+ }
+}
+