I am writing a PHP app (acting as a SAML IdP) which is trying to do a login via a SAML Response to a server (acting as the SAML SP. I am currently stuck with the server rejecting the request (I just get a 500 Bad Request).
I have written a test app (in Java/openSAML - which I'm pretty sure the server is using), and can see that the problem is that the SAML SignatureValidator validate generates
org.apache.xml.security.signature.XMLSignatureException: Invalid XMLDSIG format of DSA signature
Looking at the SAML SignatureValidator code I can see that it checks that the XMLDISG signature is exactly 40 bytes long (P1363 format?) - whereas the generated signature is 46-48 bytes long (DER ASN.1 format?).
The signature is being generated by PHP openssl_sign as below.
openssl_sign($canonicalized_signedinfo,
$signature,
$private_key,
OPENSSL_ALGO_DSS1))
An example signature (displayed as binary to hex for clarity) is as below. This is 46 bytes, but I notice it varies (depending on the random key?) from 46 to 48 bytes.
302c02146e74afeddb0fafa646757d73b43bca688a12ffc5021473dc0ca572352c922b80abd0662965e7b866416d
I can successfully verify this signature using PHP openssl_verify as below.
openssl_verify ($canonicalized_signedinfo,
$signature ,
$public_key,
OPENSSL_ALGO_DSS1))
But in my test app when I do a SignatureValidator validate (as below) I get the XMLSignatureException: Invalid XMLDSIG format of DSA signature exception.
BasicCredential credential = new BasicCredential();
credential.setPublicKey(publicKey);
credential.setUsageType(UsageType.SIGNING);
SignatureValidator sigValidator = new SignatureValidator(credential);
sigValidator.validate(signature);
Does anyone know how to do the PHP signature conversion from the 46-48 DER ASN.1 format generated by PHP openssl_sign to the 40 byte P1363 format expected by openSAML?
That resource from code project has explanations about how to convert ASN.1 format into P1363 with code examples. It may be useful to write a Java validation method.
And I propose you use this C++ code to generate a DSIG compliant signature from PHP: http://xmlsig.sourceforge.net/
By the way, it sounds more complex than simply generate the signature and validate it. You may be interested in XMLBlackbox
Related
I have an ESP8266 with Nodemcu and an AM2320 sensor.
I am sending temperature and humidity in JSON format in plain text to my HTTP server for collecting datas with PHP and SQLITE3.
That's working right.
But I wish to encrypt my datas with AES-CBC
I encrypt measures on the ESP8266 with crypto.encrypt() function and 'AES-CBC' method like this example:
https://nodemcu-firmware.readthedocs.io/en/latest/en/modules/crypto/#cryptoencrypt
I do the same encryption on my PC with Python 3 .
The resulting string matches with the result on the ESP8266 and LUA 5.1
Next I do the same encryption on my PC but with PHP 7.1
Alas, the resulting string don't match with the previous results.
I am using the 'AES-128-CBC' method on openssl_encrypt() function.
I put the script in the three languages in a gist on Github.com :
https://gist.github.com/bazooka07/bed368d313e218fcba332cb2127c70b1
That's wrong in PHP ?
Can You help me ?
I've tried your code with aes-256-cbc instead of aes-128-cbc in PHP, and it gives the same output. Changing the PHP encrypt method to aes-256-cbc should fix the problem..
I add some fix to my gist for working when the length of the message to encrypt don't mach with a multiple of 16 chars.
I try to send EBICS HPB request, but get always the same error:
EBICS_AUTHENTICATION_FAILED
I think the problem is on my code, below is what the code is doing to get the signature value :
Get signature Certificate from pem file
Remove CR, LF and Ctrl-Z
Convert PEM to DER.
Apply ISO10126
hash sha256
Encryption rsa PKCS #1 v1.5: rsa_encrypt($SignatureValue, $public_key)
Then I put the result as signature value, but I'm not sure to do the right procedure before sending my request.
I'm following several examples I've found online and I'm trying to recreate the signature they have in the official API documentation here. However, I'm failing to generate the same signature.
They state the string to sign is as follows:
GET
webservices.amazon.com
/onca/xml
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=I
temLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReview
s&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&
Version=2009-01-06
It states to "Calculate an RFC 2104-compliant HMAC with the SHA256 hash algorithm using the string above with our "dummy" Secret Access Key: 1234567890."
I do this using the following code:
$private_key = "1234567890";
$string_to_sign = "GET
webservices.amazon.com
/onca/xml
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=I
temLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReview
s&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&
Version=2009-01-06";
$signature = base64_encode(hash_hmac("sha256",$string_to_sign, $private_key, True));
This yields the following signature:
LM5S6MrycUETu1p94QDnLurKIpwiqKnCxm3B73a0QiE=
Amazon's signature is:
M/y0+EAFFGaUAp4bWv/WEuXYah99pVsxvqtAuC8YN7I=
I've followed a bunch of examples I found via Google and they all appear to do this the same way. However, I can't arrive at the same signature that Amazon gets and I can't figure out why.
Any help is appreciated.
I just burned a few hours of my life trying to figur out why my signature wasn't matching. In case this helps somebody out, use Amazon's example as mentioned by the OP. I started having trouble at steps 6 and 7. Amazon says to prepend the following three lines (including line breaks) to your string:
Get
webservices.amazon.com
/onca/xml
On my windows 7 platform, here's what I did for those steps:
$string_to_sign = "AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers%2CReviews&Service=AWSECommerceService&Timestamp=" . "2014-08-18T12%3A00%3A00Z&Version=2013-08-01";
$prepend = "GET\nwebservices.amazon.com\n/onca/xml\n";
$string_to_sign = $prepend . $string_to_sign;
Then, step 8 threw me off too because I was using the sample's secret key:
Secret Access Key: "1234567890"
instead of the correct key:
Secret Access Key: 1234567890
Hope these little mistakes don't burn anybody else.
Do you really have all those linebreaks in your actual code? Because I\ntemLookup is not the same as ItemLookup. That GET URI should be a single long string, not a multi-line string.
GET webservices.amazon.com /onca/xmlAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06
Notice how it's all on the SAME line
In PHP, I want to sign some documents with a padding of PSS, and a digest of SHA512.
According to the docs, at http://www.php.net/manual/en/function.openssl-sign.php, I can set the digest however I need, by using a string, such as
openssl_sign($text-to-sign,$binary_signature,$privkey,"sha512");
I don't see any way to set the padding, however.
Can anyone please help me understand how I can sign text, using the RSA-PSS padding style, as seen in version 2.1 of PKCS #1?
I had the same needs as you, but in 2019 (I assume we got better libraries now ;)
As you already discovered, the way is using phpseclib, which is now at version 2.0 in Debian Stretch.
Signature Generation
This is the code I used to sign some binary data, using a 8192 RSA key, with PSS padding and a SHA512 hash function:
require "/usr/share/php/phpseclib/autoload.php";
use phpseclib\Crypt\RSA;
// INPUT DATA
$privkey = "..."; // I used a RSA private key in PEM format, generated by openssl, but phpseclib supports many formats...
$mydata = "..."; // I generated some binary bytes here...
// SIGNING
$rsa = new RSA();
if ($rsa->loadKey($privkey) != TRUE) {
echo "Error loading private key";
return;
}
$rsa->setHash("sha512");
$rsa->setMGFHash("sha512"); // This NEEDS to be set, or the default will be used
$rsa->setSignatureMode(RSA::SIGNATURE_PSS); // This doesn't need to be set, as it is already the default value
$signatureBytes = $rsa->sign($mydata);
$signatureBytes is a binary string (or an array of bytes, as you call it in C/C++/C#). It doesn't have any encoding, and may contain NULL bytes. This is your wanted signature.
Side note: it's nearly required to insall php-gmp, or you'll get painfully slow signing times. Here there are some benchmarks.
Signature Verification with OpenSSL
openssl dgst -sha512 -sigopt rsa_padding_mode:pss -sigopt
rsa_pss_saltlen:-1 -verify pubkey.pem -signature signature.bin
plaintextdata.dat
In detail, the -sigopt rsa_pss_saltlen:-1 tells openssl to use a salt length that matches the hashing algorithm size. (this is what the Microsoft APIs do, and there's no way to change that behavior)
Signature Verification with C++/CLI (.NET)
In order to be able to use the RSA public key in the .NET world, without using any other library, you need first to export it in BLOB format using openssl:
openssl rsa -pubin -inform PEM -in pubkey.pem -outform "MS PUBLICKEYBLOB" -out pubkey.blob
Then, you need .NET 4.6, with the new RSACng CryptoProvider.
And that's the code:
// Import the public key (as BLOBDATA)
RSACryptoServiceProvider^ rsaCsp = gcnew RSACryptoServiceProvider();
rsaCsp->ImportCspBlob(pubkey);
RSAParameters rsaParams = rsaCsp->ExportParameters(false);
RSA^ rsa = gcnew RSACng();
rsa->ImportParameters(rsaParams);
array<Byte>^ dataBytes = ...;
array<Byte>^ signatureBytes = ...;
bool signIsValid = rsa->VerifyData(dataBytes, signatureBytes, HashAlgorithmName::SHA512, RSASignaturePadding::Pss);
In order not to be "That Guy", I thought I'd leave an answer to the question, given how this does show up in Google and all ;)
I am able to get the PSS padding via http://phpseclib.sourceforge.net/
So far, I haven't gotten it to interop with OpenSSL or LibTomCrypt, but..
I'm probably just configuring it wrong.
I'm sure you'll have better luck, future person!
-CPD
I am using PGP (GNU Privacy Guard) for encrypting the file.
while encrypting i removed the '.pgp' extension of encrypted file.
Now some how i want to know which file is already encrypted in the specific folder.
Note :- my goal is that ... do not encrypt any file twice ... so before encrypt any file .. i want to check is the file already encrypted.
in php can we find out which file is already encrypted ?
PGP file all starts with "-----BEGIN PGP MESSAGE-----".
So you can do something like this:
$content = file_get_contents($filename);
$encrypted = strpos($content, '-----BEGIN PGP MESSAGE-----') === 0;
I really don't know much about how it works, or how you could look at the contents of the file to tell if it is encrypted properly, but could you try decrypting them? If you know you're only working with plain text files, you could examine the first 500 bytes of the decrypted data and if there's strange characters (outside the standard a-z A-Z 0-9 + punctuation, etc), then that could be a clue that the file wasn't encrypted.
This really is a half-arsed answer, I know, but it was a bit long to fit into a comment.
You can't unless you understand the algorithm used in the encryption. Once you understand it, you can apply that to check whether a file is already encrypted.
Also check to make sure that there is already a function available in PGP for checking if something is already encrypted. This is usually present in encryption solutions.
Thanks
There are two possible formats for OpenPGP data, binary and ascii armored.
Ascii-armored files are easy to recognize by looking for "-----BEGIN PGP MESSAGE-----" which can also be done using the unix command file:
$ file encrypted
encrypted: PGP message
#ZZ_Coders answer is totally fine if you're only dealing with ascii armored encrypted files.
If it shows something else, it's not an OpenPGP message - or in binary format. This isn't as easy to recognize (at least I don't know which magic packets you could look for), but you can easily use the gpg command to test the file:
$ gpg --list-only --list-packets encrypted
:pubkey enc packet: version 3, algo 1, keyid DEAFBEEFDEADBEEF
data: [2048 bits]
:encrypted data packet:
length: 73
mdc_method: 2
If it isn't encrypted, response will look like this:
$ gpg --list-only --list-packets something_else
gpg: no valid OpenPGP data found.
In PHP, you could use this code to check if a file is OpenPGP-encrypted:
if (strpos(`gpg --list-only --list-packets my_file.txt 2>&1`,
'encrypted data packet'))
echo "encrypted file";