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

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.

Related

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

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.

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);

Using Zend_Mail_Transport_Smtp with an MD5-Hashed value as password

I'd like to offer the users ob my web-application the possibility to send out emails using our smtp-server.
The password for the user accounts are md5-hased and the smtp-server is hashing the received values to check for the right username-password kobination.
Now i'm looking for a good way to set up Zend_Mail_Transport_Smtp - I obviously need the plain-text password and forward it to the smtp-server which then converts it to a md5-hash.
But that means that i have to store the users password somewhere in plaintext, which i'd like to avoid.
Are there any best practices on how to set up an webmailer using the zend framework?
The only idea i had was to save the unhashed password in a session (the user accounts in my application are linked with the mail server accounts), but there has to be a better way to handle this situation
What you can do is to store the password in a encoded format in the database and decode it in your application when you need it. Unfortunately MD5 is just a hashing function and you cannot decode to the plain password. I know three ways to accomplish this:
Substitute letters:
You can use something like ROT13 to replace letters in your plain password:
// store this in the database
$pw_rot = str_rot13( "plain_password" );
// use this in the application
$pw_plain = str_rot13( "cynva_cnffjbeq" );
I wouldn't recommend to use str_rot13() or something like this, because is easily guessed by someone who sees the password.
Decode/encode without a key:
Another way is to decode/encode the password with a function, which doesn't need a key like Base64:
// store this in the database
$pw_base64 = base64_encode( "plain_password" );
// use this in the application
$pw_plain = base64_encode( "cGxhaW5fcGFzc3dvcmQ=" );
A little bit better then the above, but I would use that only for testing purposes, because it's easily implemented and to use.
Decode/encode with a key:
A better way is to use key and a symmetric block cipher like Blowfish:
class Password {
const KEY = 'your_secret_key_for_the_cipher';
// encode the plain text with key for storing in the database
public function encode( $plain_text ) {
// set up the environment
$td = mcrypt_module_open( MCRYPT_BLOWFISH, '', MCRYPT_MODE_ECB, '' );
$key = substr( self::KEY, 0, mcrypt_enc_get_key_size( $td ) );
$iv_size = mcrypt_enc_get_iv_size( $td );
$iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
if( mcrypt_generic_init( $td, $key, $iv ) != -1 ) {
$cipher_text = mcrypt_generic( $td, $plain_text );
// clean up the mcrypt enviroment
mcrypt_generic_deinit( $td );
mcrypt_module_close( $td );
}
// use hex value
return bin2hex( $cipher_text );
}
// decode the stored cipher text with key to use in the application
public function decode( $cipher_text ) {
// set up the environment
$td = mcrypt_module_open( MCRYPT_BLOWFISH, '', MCRYPT_MODE_ECB, '' );
$key = substr( self::KEY, 0, mcrypt_enc_get_key_size( $td ) );
$iv_size = mcrypt_enc_get_iv_size( $td );
$iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
if( mcrypt_generic_init( $td, $key, $iv ) != -1 ) {
$plain_text = mdecrypt_generic( $td, pack( "H*" , $cipher_text ) );
// clean up the mcrypt environment
mcrypt_generic_deinit( $td );
mcrypt_module_close( $td );
}
// remove NUL which maybe added by padding the plain_text
return rtrim( $plain_text, "\0" );
}
With this way only someone who has access to the database and the source code can decode the password. On the down side you have a more complex application and little bit performance impact. Also you can other symmetric block cipher.
And the most important: Never store plain passwords.

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;
}