Conversion of public key parameters from Crypt::OpenSSL::RSA into JWK failed - perl

I was creating a mock server for OAuth2 and I was stuck on during the generation of a JWK token, more specifically, I failed to understand how to convert the e and n parameters
I have this code:
use strict;
use v5.26.0;
use Data::Dumper; # or DDP if have i t installed!
use Crypt::JWT qw(encode_jwt decode_jwt);
use Crypt::OpenSSL::RSA;
use Crypt::OpenSSL::Bignum; # needs to be installed in order to run $rsa_pub->get_key_parameters();
use MIME::Base64;
# don't worry, this is just a test/mock server with 'random' generated RSA pair
my $rsa_pvt = Crypt::OpenSSL::RSA->new_private_key(
'-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0LdLaW9i0NUVwV1+ZSnwwYebLliFLCEQjzsklGww0fYa0VWM
kEl/WD1mam/idqqAadkgMKzZY4JWx824rRpHOosTJbOMHKa1cRA933yv8Ywzahmx
ih9Jx/QjK2VHSlxTRq2cKN/E9S2VN64PBlBc2LsoAzM9M4r9X90KJFi3+VazjDZ+
9iWA5UCqrWcGrEy21ZVLF27Dm03STIewtV9goBn/7Gd5sMfDfnvMLKSK5ZRbGigt
fkjS91qZUiHjU+WXJrMS2mTr/W9fmAJ7R9jQ09wpJVqP+UOnFjL/0mesAQ5H4FTt
RoXhYiKF3zqckRkAwRuP3sv2nCGA8MoiZR5rFwIDAQABAoIBAFpoSz4shX04D+hm
ey2O8T6jYtC8f1MSL34bfEjeZHdOR2eNywllDMhIMGjCdjI4wM8YwhzTgobcGoMJ
1YkF7Pyq6WxXTcXLYKTNCEAaXowe0taOspzF2MvIMMPHZw4K1/exlAcQhtw9Fnm7
574waUdoKnjYZRZCimZP9OixlV9nKrD36lytd1HewwcD+nm0whMq4Ud5W9AyGwwM
xJR9++srSiqi6wpj5p1cP73UgpdOn293foATV3NBjTBjndk/q1IoO8ylv+rn2Pwi
iFYGHUIrLsBBBYYPclEggP09M/r4vLlVhMpvf+0UBOodRLk5QuGsPlk3ch/jfSpf
f1JNLYECgYEA6lf4yjim+U3LumPKYzhM0zsQf5Qvp0pGb/7XWw3WdWc3hcnJevMp
uL1k6Qyg3186K+loJqdrsDqOQhZO2BBA8TMcsgiiEk2pTl5InSIf/aedNzuClX0D
gxjgOTMsp+9G6YafcvzG+5gCuFM0ONqyyC+mOl55ICSF0HPgN7TaUhECgYEA5AEI
LoxwPs4fXmXmpNg/2JD8upwl8GdEiDs4yaY2kE+5ocvtaNn6qi3kO5cDIAmkUqFE
WwjFMD2sROROtpivrlb3+GJbrjbmY27KrEFd5RkNRCArZV+sUNt/s7RrvNOVvJ9z
e82xYrNBXhpFhnaI4oLSN9ZWjx+mroX2M65iwqcCgYEAzzForqLYPqQh9HI2hvNI
OZqHQ8VpPKfXDz5qef8KFlNkK831bdeAk+4gQk0AD37Kl/iONV7hP7cGADhpDW+R
e7CNNnubkENJ5hhGa2e4kTSZNDRRiIo3iLl9xhUQ7ooIUIDOiYQlCl2kSgSGr53t
ZEF83y6YOWsRRPSu0ZH9VYECgYA4A/zjmsM02uUgBv78Ptioty4wFo7HmkdfBNW1
zO0Y1U1w7637FZqc1rt83GP7KgNB/bbSerwfVveM0V55Q9fdiCZR0rBdg8VkZmLK
oSCVWWtF8nVW6YNnNhYQq2HQuVbPSYlQwD81VX7YxLGSEGse4y8MYs9PSGJl/Cl5
lv1SfwKBgFE8zTWFAnMOBczF0VCl0hpUaIknlTwvL6iGf5Vl4M4XBKE8KEkjE+gE
H7lxZ8XxxVHrvm++BZZk5uSmIWGUZlJuRNLQDiRxLqNOAaG12YGyg/eTJOzkvzfL
U8UQV8ROVyZR1Rw8ya9ksAmlDjy55VOXHT9pvHd/FjQMcBeh9lE8
-----END RSA PRIVATE KEY-----'
);
my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key(
'-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA0LdLaW9i0NUVwV1+ZSnwwYebLliFLCEQjzsklGww0fYa0VWMkEl/
WD1mam/idqqAadkgMKzZY4JWx824rRpHOosTJbOMHKa1cRA933yv8Ywzahmxih9J
x/QjK2VHSlxTRq2cKN/E9S2VN64PBlBc2LsoAzM9M4r9X90KJFi3+VazjDZ+9iWA
5UCqrWcGrEy21ZVLF27Dm03STIewtV9goBn/7Gd5sMfDfnvMLKSK5ZRbGigtfkjS
91qZUiHjU+WXJrMS2mTr/W9fmAJ7R9jQ09wpJVqP+UOnFjL/0mesAQ5H4FTtRoXh
YiKF3zqckRkAwRuP3sv2nCGA8MoiZR5rFwIDAQAB
-----END RSA PUBLIC KEY-----'
);
my ($n, $e, #o) = $rsa_pub->get_key_parameters();
# I think something is wrong in this conversion
$n = encode_base64($n->to_bin(), '');
$e = encode_base64($e->to_bin(), '');
my $kids = {
"keys" => [
{
"kty" => "RSA",
"kid" => "rsa1",
"alg" => "RS256",
"use" => "sig",
"n" => $n,
"e" => $e
}
]
};
say "Keys parameters";
say Dumper($kids);
my $encoded = encode_jwt(
payload => {a => 'b'},
alg => 'RS256',
key => $rsa_pvt,
extra_headers => {'kid' => 'rsa1'},
);
say "Generated JWT: $encoded";
my $decoded_with_pub = decode_jwt(
token => $encoded,
key => $rsa_pub,
);
say "Decoded with pub certificate";
say Dumper($decoded_with_pub);
say "Trying to decode with certificate from parameters";
# do not returns,
# Use of uninitialized value in unpack at ~/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Crypt/PK/RSA.pm line 102.
my $decoded_with_kid = decode_jwt(
token => $encoded,
kid_keys => $kids,
);
The Crypt::JWT is working when I test with the production keys https://sso.staging.acesso.gov.br/jwk
The e parameter AQAB (same recommended exponent of 65537) is matching, so I don't know how else I should convert the n from BigInt into Base64 any different than the way I did
SEO:
Use of uninitialized value in unpack at ~/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Crypt/PK/RSA.pm line 102.

Related

openssl_seal fails error:0E06D06C:configuration file routines:NCONF_get_string:no value

I am trying to seal/open a file. Encryption fails and the following error is generated.
error:0E06D06C:configuration file routines:NCONF_get_string:no value
Here's a code sample that can reproduce the issue
// Generate key pair and keep them safe...
$key = openssl_pkey_new([
'private_key_bits' => 4096,
'private_key_type' => OPENSSL_KEYTYPE_EC,
'curve_name' => 'prime256v1',
]);
$privKey = null;
openssl_pkey_export($key, $privKey);
$pubKeyDetails = openssl_pkey_get_details($key);
$pubKey = $pubKeyDetails['key'];
openssl_free_key($key);
// Load the pubkey to encrypt
$key = openssl_pkey_get_public($pubKey);
$data = file_get_contents('of-some-pretty-large-file');
// ----- This here fails -----
if (openssl_seal($data, $sealed, $eKeys, [$key]) === false) {
echo "Encryption failed\n";
echo openssl_error_string() . "\n";
exit;
}
openssl_free_key($key);
$key = openssl_pkey_get_private($privKey);
if (openssl_open($sealed, $decryptedData, $eKeys[0], $key)) {
echo ($decryptedData === $data ? "Matched\n" : "Trash\n");
}
openssl_free_key($key);
The error message
error:0E06D06C:configuration file routines:NCONF_get_string:no value
is not caused by openssl_seal, but by openssl_pkey_new. This does not affect the functionality, i.e. the key is generated successfully, see here and here. This also applies to the posted code, which generates a private EC key in SEC1 format and a public key in X.509 format.
The PHP method openssl_seal is based on the OpenSSL functions EVP_SealInit, EVP_SealUpdate and EVP_SealFinal, here. In the corresponding OpenSSL documentation the following is described in the Notes-section:
The public key must be RSA because it is the only OpenSSL public key algorithm that supports key transport.
This means that openssl_seal only works with RSA or an RSA key. If the following is used in the posted code:
$key = openssl_pkey_new([
'private_key_bits' => 4096,
'private_key_type' => OPENSSL_KEYTYPE_RSA
]);
decryption and encryption work as expected.

Perl - Crypt::OpenSSL::RSA->new_public_key "Error : unrecognized key format"

I'm trying to generate a token for an Http request call based on a private key generated using OpenSSL RSA (2048) and then a public key, which is generated by the group that has the API we're calling. One side of the token string is encrypted using the public key the other with the private key. The private key encryption works with the code below but calling the Crypt::OpenSSL::RSA->new_public_key(public key string) fails with the "Error : unrecognized key format" issue. I'm not sure what's wrong with the key but given that the other one works (and was supposedly generated using same OpenSSL unix tool) it seems odd.
Any help would be greatly appreciated.
Thanks,
Dave
use strict;
use Time::HiRes qw( gettimeofday );
use Crypt::OpenSSL::RSA;
use Crypt::OpenSSL::X509;
use Crypt::OpenSSL::Random;
use MIME::Base64;
use DateTime;
my $sysTime=undef;
my $sTime=undef;
my $sAPIToken;
my $EDMPrivateKey=qq~
-----BEGIN PRIVATE KEY-----
<private key base 64>
-----END PRIVATE KEY-----
~;
my $DMPublicKey=qq~
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsftmdt4aAZ7B1F7wVjIz0RSO6w1bIX/4
<rest of public key>
-----END PUBLIC KEY-----
~;
sub setTime() {
my $ms;
my $ms2;
my $microSec;
my $tz;
($sysTime,$microSec) = gettimeofday();
# $sysTime=gettimeofday();
$ms=sprintf("%.*s",3,$microSec);
$ms2=$microSec-($ms*1000);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($sysTime);
$sTime=sprintf("%04d-%02d-%02dT%02d:%02d:%02d.%03d-%04d",
$year+1900,$mon+1,$mday,$hour,$min,$sec,$ms,$tz);
$sysTime=$sysTime*1000+$ms;
}
sub setTimeNew() {
my $ms;
my $ms2;
my $microSec;
($sysTime,$microSec) = gettimeofday();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($sysTime);
$ms=sprintf("%.*s",3,$microSec);
$ms2=$microSec-($ms*1000);
my $dt=DateTime->new(
year => $year+1900,
month => $mon+1,
day => $yday,
hour => $hour,
minute => $min,
second => $sec,
nanosecond => $microSec*1000
);
$sysTime=$sysTime*1000+$ms;
$sTime=$dt->strftime("%C%y-%m-%dT%H:%M:%S.%3N%z");
}
sub main() {
my $sTokenPt1;
my $sTokenPt2;
my $sPubStr;
my $rsaPublic = Crypt::OpenSSL::RSA->new_public_key($DMPublicKey);
$rsaPublic->use_sha1_hash();
setTimeNew();
$sTokenPt1 = encode_base64($rsaPrivate->encrypt($sysTime));
$sTokenPt2 = encode_base64($rsaPublic->encrypt($sTime));
printf "%d\n%s\n",$sysTime,$sTime;
$sAPIToken = sprintf("%s,%s",$sTokenPt1,$sTokenPt2);
printf "%d\n%s\nTTOKEN=%s\n",$sysTime,$sTime,$sAPIToken;
}
eval {
main();
};
if ($#) {
printf "Error : %s\n",$#;
}

Error Connecting to a JMS Topic Broker via SSL using Net::STOMP::Client module in Perl

I am trying to connect to a TIBCO JMS Topic Broker with the help of Net::STOMP::Client package in perl.
I am using the SSL approach while creating a new Net::STOMP::Client Object and passing the attributes 'uri' & 'sockopts' where it takes the Topic Uri & SSL Certificate files for Authentication.
When I try to run this script it throws an error saying : -
cannot SSL connect to mmx-nprd1-06:7222: IO::Socket::INET6 configuration failed
Code is give below :-
use Net::Stomp;
use Net::STOMP::Client;
use Moose;
use strict;
use warnings;
use FindBin qw($Bin);
print "\n$Bin\n";
my $stomp;
$stomp = Net::STOMP::Client->new(
uri => "stomp+ssl://mmx-nprd1-06:7222",
sockopts => {
# path of the directory containing trusted certificates
SSL_ca_path => "$Bin/JmsCertificate/",
# client certificate to present
SSL_cert_file => "$Bin/JmsCertificate/aix_jms_cert.pem",
# # client private key
SSL_key_file => "$Bin/JmsCertificate/aix_jms_key.pem",
# passphrase of the client private key
SSL_passwd_cb => sub { return("password") },
},
);
$stomp->connect();
my $peer = $stomp->peer();
printf("connected to broker %s (IP %s), port %d\n", $peer->host(), $peer->addr(), $peer->port());
my $sid = $stomp->uuid();
$stomp->subscribe(
destination => "/queue/test",
# we use the generated subscription id
id => $sid,
# we want a receipt on our SUBSCRIBE frame
receipt => $stomp->uuid(),
);
my $count = 0;
my $frame;
while ($count < 10) {
$frame = $stomp->wait_for_frames(timeout => 1);
if ($frame) {
if ($frame->command() eq "MESSAGE") {
$count++;
printf("received message %d with id %s\n",
$count, $frame->header("message-id"));
} else {
# this will catch the RECEIPT frame
printf("%s frame received\n", $frame->command());
}
} else {
print("waiting for messages...\n");
}
}
$stomp->unsubscribe(id => $sid);
$stomp->disconnect();
Can Someone help me out with this as I am not able to figure out whats going wrong here.
I also had some issues using the uri key try the following :
$stomp = Net::STOMP::Client->new(host => "mmx-nprd1-06", port => 7222,
sockopts => {
# path of the directory containing trusted certificates
SSL_ca_path => "$Bin/JmsCertificate/",
# client certificate to present
SSL_cert_file => "$Bin/JmsCertificate/aix_jms_cert.pem",
# # client private key
SSL_key_file => "$Bin/JmsCertificate/aix_jms_key.pem",
# passphrase of the client private key
SSL_passwd_cb => sub { return("password") }, # Leave this out if your key doesn't require a passphrase
},
);
$stomp->connect(login => "aix_jms", passcode => "some_password");

Perl SSLeay certificate and key as string

I have some problem with SSLeay in Perl. And I've no experience of Perl to speak of so I need your help!
I'm trying to communicate whit Apple Push Notification Service (APNS). That's kind of easy, but I can't use files to store the certificate and the RSA private key. This code should be run from a database.
This works:
Net::SSLeay::CTX_use_RSAPrivateKey_file( $ctx, 'key.pem', &Net::SSLeay::FILETYPE_PEM );
die_if_ssl_error("private key");
Net::SSLeay::CTX_use_certificate_file( $ctx, 'cert.pem', &Net::SSLeay::FILETYPE_PEM );
die_if_ssl_error("certificate");
But as I said earlier, I cannot use files. So I tried this:
$private_key = '----BEGIN RSA PRIVATE [...]';
my $rsa_private = Crypt::OpenSSL::RSA->new_private_key($private_key);
Net::SSLeay::CTX_use_RSAPrivateKey( $ctx, $rsa_private );
die_if_ssl_error("private key");
Net::SSLeay::CTX_use_certificate_file( $ctx, 'cert.pem', &Net::SSLeay::FILETYPE_PEM );
die_if_ssl_error("certificate");
But now I get Segmentation fault at row 7. If I exit at row 6, no error occurs.
What am I doing wrong at this step?
Next step is to get the certificate from a string as well. The documentation for SSLeay says the second parameter of CTX_use_certificate must be a x509 object. So I try to create one:
my $private_key = '----BEGIN RSA PRIVATE [...]';
my $certificate = '----BEGIN CERTIFICATE [...]';
my $rsa_private = Crypt::OpenSSL::RSA->new_private_key($private_key);
Net::SSLeay::CTX_use_RSAPrivateKey( $ctx, $rsa_private );
die_if_ssl_error("private key");
my $x509 = Crypt::OpenSSL::X509->new_from_string($certificate);
Net::SSLeay::CTX_use_certificate( $ctx, $x509 );
die_if_ssl_error("certificate");
But that gives me:
certificate 9530: 1 - error:140BF10C:SSL routines:SSL_SET_CERT:x509 lib
9530: certificate
Do you have any tip or idea how to solve this problem?
this should work if Net::SSLeay version is equal or higher then 1.45
my $pkey = '----BEGIN PRIVATE KEY ... (PEM formated)';
my $cert = '----BEGIN CERTIFICATE ... (PEM formated)';
my $bio_key = Net::SSLeay::BIO_new(Net::SSLeay::BIO_s_mem()) or die;
Net::SSLeay::BIO_write($bio_key, $pkey) or die "no key";
my $evp_pkey = Net::SSLeay::PEM_read_bio_PrivateKey($bio_key,
sub { RETURN PASSWORD IF ANY NESSESARY}) or die "no evp_pkey structure";
Net::SSLeay::CTX_use_PrivateKey($ctx, $evp_pkey);
Net::SSLeay::BIO_free($bio_key);
my $bio_cert = Net::SSLeay::BIO_new(Net::SSLeay::BIO_s_mem()) or die;
Net::SSLeay::BIO_write($bio_cert, $cert) or die "no cert";
my $x509 = Net::SSLeay::PEM_read_bio_X509($bio_cert) or die "no x509 structure";
Net::SSLeay::CTX_use_certificate($ctx, $x509) or die ;
Net::SSLeay::BIO_free($bio_cert);

How can I decrypt Blowfish ciphertext with a salted header?

I have some ciphertext that has been encrypted using Perl's Crypt::CBC module that I wish to decrypt elsewhere.
The ciphertext was generated using the 'simple' version of the Crypt::CBC constructor, that is:
use Crypt::CBC;
$cipher = Crypt::CBC->new( -key => 'my secret key',
-cipher => 'Blowfish'
);
From reading the MAN page, this method of construction will take the simple string key and random salt to generate an IV & literal key to use for encryption, as well as embed a header with the salt.
"salt" -- Combine the passphrase with
an 8-byte random value to
generate both the block cipher key and the IV from the
provided passphrase. The salt will be appended to the
beginning of the data stream allowing decryption to
regenerate both the key and IV given the correct passphrase.
This method is compatible with current versions of OpenSSL.
I now need to decrypt the ciphertext on another platform that only supports CBC decryption given the ciphertext, a literal key & IV. To attempt to generate the literal key, IV & salt, I used Crypt::CBC to generate the values like so:
my $crypt = new Crypt::CBC(-key => 'my secret key', -cipher => 'Blowfish');
my $out = $crypt->decrypt($ciphertext);
my $literal_key = $crypt->key();
my $iv = $crypt->iv();
my $salt = $crypt->salt();
The decryption here is correct, but I've been unable to use the generated literal key & IV to decrypt the cipher; this produces rubbish:
my $crypt2 = new Crypt::CBC(
-literal_key => 1,
-key => $literal_key,
-cipher => 'Blowfish',
-iv => $iv,
-header => 'none');
my $rubbish - $crypt2->decrypt($ciphertext);
I can't provide a literal key and use a salted header so I'm lost as to the next move.
How can I decrypt this text?
EDIT: The target system is not running Perl, but I have been able to generate the identical value as in $rubbish above, so I'm sure it's using the same algorithm (CBC, Blowfish) to decipher.
To decrypt the stream, you first need to remove the header added by Crypt::CBC's "salt" mode. The header consists of the 8 characters Salted__ followed by 8 bytes of salt data.
In perl, something like this should do it:
my $crypt2 = new Crypt::CBC(
-literal_key => 1,
-key => $literal_key,
-cipher => 'Blowfish',
-iv => $iv,
-header => 'none');
my $cleartext = $crypt2->decrypt(substr($ciphertext, 16));
This may work. Your key will have to be exactly 56 bytes in length and the iv will have to be exactly eight bytes long:
#!/usr/bin/perl
use strict;
use warnings;
use Crypt::CBC;
my $key = "x" x 56;
my $iv = "x" x 8;
my $plaintext = "this is just some normal text\n";
my $ciphertext = Crypt::CBC->new(
-cipher => 'Blowfish',
-header => 'none',
-literal_key => 1,
-key => $key,
-iv => $iv,
)->encrypt($plaintext);
print Crypt::CBC->new(
-cipher => 'Blowfish',
-header => 'none',
-literal_key => 1,
-key => $key,
-iv => $iv,
)->decrypt($ciphertext);
If anybody needs the _salted_key_and_iv function in PHP - here it is:
function _salted_key_and_iv ($pass, $salt) {
if(strlen($salt) != 8) {
die("Salt must be 8 bytes long");
}
$key_len = 56;
$iv_len = 8;
$desired_len = $key_len+$iv_len;
$data = '';
$d = '';
while (strlen($data) < $desired_len) {
$d = pack("H*", md5($d . $pass . $salt));
$data .= "$d";
}
return $data;
}