use OpenSSL::RSA; use OpenSSL::Bio; use OpenSSL::PEM; use OpenSSL::X509; use OpenSSL::EVP; use OpenSSL::Digest; use NativeCall; class OpenSSL::RSAKey { has $.private; has $.rsa; submethod DESTROY { OpenSSL::RSA::RSA_free($!rsa); } method new(:$private-pem, :$public-pem, :$x509-pem) { if $private-pem { # `$bio-buf` contains a C pointer to `$private-pem-buf`. So `$private-pem-buf` needs to stay alive as long # as `$bio-buf` does. my $private-pem-buf = $private-pem.encode; my $bio-buf = OpenSSL::Bio::BIO_new_mem_buf($private-pem-buf, $private-pem-buf.bytes); my $rsa = OpenSSL::PEM::PEM_read_bio_RSAPrivateKey($bio-buf, OpaquePointer, OpaquePointer, OpaquePointer); OpenSSL::Bio::BIO_free($bio-buf); die "Unable to read key" unless defined($rsa); return self.bless(:$rsa, :private(True)); } elsif $public-pem { # `$bio-buf` contains a C pointer to `$public-pem-buf`. So `$public-pem-buf` needs to stay alive as long # as `$bio-buf` does. my $public-pem-buf = $public-pem.encode; my $bio-buf = OpenSSL::Bio::BIO_new_mem_buf($public-pem-buf, $public-pem-buf.bytes); my $rsa; if $public-pem ~~ /RSA/ { $rsa = OpenSSL::PEM::PEM_read_bio_RSAPublicKey($bio-buf, OpaquePointer, OpaquePointer, OpaquePointer); } else { $rsa = OpenSSL::PEM::PEM_read_bio_RSA_PUBKEY($bio-buf, OpaquePointer, OpaquePointer, OpaquePointer); } OpenSSL::Bio::BIO_free($bio-buf); die "Unable to read key" unless defined($rsa); return self.bless(:$rsa, :private(False)); } elsif $x509-pem { # `$bio-buf` contains a C pointer to `$x509-pem-buf`. So `$x509-pem-buf` needs to stay alive as long # as `$bio-buf` does. my $x509-pem-buf = $x509-pem.encode; my $bio-buf = OpenSSL::Bio::BIO_new_mem_buf($x509-pem-buf, $x509-pem-buf.bytes); my $x509 = OpenSSL::PEM::PEM_read_bio_X509($bio-buf, OpaquePointer, OpaquePointer, OpaquePointer); OpenSSL::Bio::BIO_free($bio-buf); die "Unable to read key" unless defined($x509); my $evp-key = OpenSSL::X509::X509_get_pubkey($x509); OpenSSL::X509::X509_free($x509); die "Unable to read key" unless defined($evp-key); my $rsa = OpenSSL::EVP::EVP_PKEY_get1_RSA($evp-key); OpenSSL::EVP::EVP_PKEY_free($evp-key); die "Unable to read key" unless defined($rsa); return self.bless(:$rsa, :private(False)); } else { die "Please pass one of private-pem, public-pem, x509-pem.\nNo other formats are currently supported."; } } method sign(Blob $blob, :$sha1, :$sha256) { die "Must have private key to sign" unless $.private; my $hashed; my $type; if $sha256 { $hashed = sha256($blob); $type = 672; # NID_sha256 } else { $hashed = sha1($blob); $type = 64; # NID_sha1 } my $sig = buf8.new; my $slen = CArray[int32].new; $slen[0] = OpenSSL::RSA::RSA_size($.rsa); $sig[$slen[0]-1] = 0; my $ret = OpenSSL::RSA::RSA_sign($type, $hashed, $hashed.bytes, $sig, $slen, $.rsa); die "Failed to sign" unless $ret == 1; return $sig.subbuf(0, $slen[0]); } method verify(Blob $blob, Blob $sig, :$sha1, :$sha256) { my $hashed; my $type; if $sha256 { $hashed = sha256($blob); $type = 672; # NID_sha256 } else { $hashed = sha1($blob); $type = 64; # NID_sha1 } my $ret = OpenSSL::RSA::RSA_verify($type, $hashed, $hashed.bytes, $sig, $sig.bytes, $.rsa); return True if $ret == 1; return False; } }