MakeSignature.signDetached throws No Such Algorithm exception (SUN provider & SHA-256) - itext

I am trying to digitally sign a PDF document with iText. Since I will be using a hardware encryptor with JCE provider eventually I am also trying to test getting rid of BountyCastle in my unit test and instead use the default SUN implementation for the time being (until the hardware encryptor arrives).
However, I get the following exception when I run my program:
Exception in thread "main" java.security.NoSuchAlgorithmException: no such algorithm: SHA256 for provider SUN
at sun.security.jca.GetInstance.getService(GetInstance.java:87)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at java.security.Security.getImpl(Security.java:698)
at java.security.MessageDigest.getInstance(MessageDigest.java:215)
at com.itextpdf.text.pdf.security.DigestAlgorithms.getMessageDigest(DigestAlgorithms.java:159)
at com.itextpdf.text.pdf.security.ProviderDigest.getMessageDigest(ProviderDigest.java:61)
at com.itextpdf.text.pdf.security.MakeSignature.signDetached(MakeSignature.java:130)
at SignDoc.signPdf(SignDoc.java:142)
at SignDoc.main(SignDoc.java:182)
The last few lines of codes are as below:
//ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "BC");
//ExternalDigest digest = new BouncyCastleDigest();
//ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "SUN");
ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "SunRsaSign");
ExternalDigest digest = new ProviderDigest("SUN");
MakeSignature.signDetached(appearance, digest, es, chain, null, null, null, 0, CryptoStandard.CMS);
As you can see, I am just copying the samples and changed the provider name and the call 'new BouncyCastleDigest()' to 'new ProviderDigest("SUN")'
Peeking at the source codes of iText (5.5.1-SNAPSHOT), I find the followings code fragments suspicious:
MakeSignature.java line 142 - 145
hashAlgorithm = externalSignature.getHashAlgorithm() is called and then used in
DigestAlgorithms.digest(data, externalDigest.getMessageDigest(hashAlgorithm));
PrivateKeySignature.java line 76
Since I am using PrivateKeySignature, I peek at PrivateKeySignature.java and find that it returns its private class member hashAlgorithm and this is how it obtains the value during Construction (line 76):
this.hashAlgorithm =
DigestAlgorithms.getDigest(DigestAlgorithms.getAllowedDigests(hashAlgorithm));
DigestAlgorithms.java methods getAllowedDigests() and getDigest()
In turn, getAllowedDigests() returns the OID of the algorithm if the algorithm name is found in the allowedDigests hash map (2.16.840.1.101.3.4.2.1 in my case), otherwise it returns null.
getDigest gets the digest name back from the OID using the digestNames hash map.
However, the name in digestNames hash map that corresponds to the OID is SHA256, not SHA-256.
As a result, the final digest name got was "SHA256" as opposed to "SHA-256" and "SHA256" was causing the NoSuchAlgorithm exception in SUN provider.
(I tried getting a message digest instance directly using the SUN provider. It succeeded for SHA-256 but threw the same exception I am reporting here for SHA256.
Is this a problem with iText when using JCE providers other than BC?
Appreciate if anyone could shed some light on my problem.

As a temporary workaround, I added the followings to PrivateKeySignature.java:
// Temporary fix - to remove hyphenation in hashAlgorithm name
// String signMode = hashAlgorithm + "with" + encryptionAlgorithm;
String signMode = hashAlgorithm.replaceAll("-","") + "with" + encryptionAlgorithm;
// End Temporary fix
It works for me at least for now. Has to investigate how standard names of different algorithms in different context go before a long term solution can be devised, I believe.

Related

Question about Crypt::OpenSSL::RSA->verify method

My question is about this:
https://metacpan.org/pod/Crypt::OpenSSL::RSA
If there described method verify() fails, I do error handling like this:
my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($x509PubKey);
logm("exception: my err msg...") unless $rsa_pub->verify($text, $signature);
But is it possible get exact reason why verification failed?
I'm not sure that getting "the exact reason why verification failed" makes sense as a question. To verify a signature you specify:
the signature algorithm
the padding algorithm
the hashing function
Ultimately the signature is just a number that was computed by padding the the plaintext input, hashing the resulting bytes and performing a mathematical calculation using the private key.
Verifying the signature involves taking the plaintext, padding it, hashing it, and performing a mathematical calculation using the public key to produce another number which is then compared to the number from the signature (using modulo arthimetic?). If the numbers are the same then the signature is valid if they're different, it's not.
All of which is a roundabout way of saying if the verify method returns false then assuming you're using the correct public key, one of these things must be different:
the plaintext
the signature algorithm
the padding algorithm
the hashing function
But there's really no way of knowing which. It's like saying "I'm trying to multiply two numbers to get 42, but I don't get 42, which of the numbers is wrong?".
Here are a couple of signature verification functions for common combinations of algorithms (which I wrote for Authen::NZRealMe::XMLSig):
sub _verify_signature_rsa_sha1 {
my($self, $plaintext, $bin_sig) = #_;
my $rsa_pub_key = Crypt::OpenSSL::RSA->new_public_key($self->pub_key_text);
$rsa_pub_key->use_pkcs1_padding();
$rsa_pub_key->use_sha1_hash();
return $rsa_pub_key->verify($plaintext, $bin_sig);
}
sub _verify_signature_rsa_sha256 {
my($self, $plaintext, $bin_sig) = #_;
my $rsa_pub_key = Crypt::OpenSSL::RSA->new_public_key($self->pub_key_text);
$rsa_pub_key->use_pkcs1_oaep_padding();
$rsa_pub_key->use_sha256_hash();
return $rsa_pub_key->verify($plaintext, $bin_sig);
}
The context for the above code is signed sections of XML documents, which has the added complexity of needing to use the right canonicalization and encoding and also the signature data is Base64 encoded so needs to be decoded into bytes first.
The information about which padding and hashing algorithms to use should be available from the spec for the source data you're working with, but if not I guess you could try random combinations.

How to decrypt safeNet wrapped exported keys?

I have exported a 3DES key from my SafeNet HSM to a file using a tool named KMU. This tool wraps the key before extraction using another 3DES key. I have access to the plain-text value of the second key.
The question is "how can I decrypt the wrapped file to obtain plain-text value of the wrapped key?"
Update:
FYI: The final exported file looks like this:
L1: 000001f4 000001a800000001000001a0
L2: 00000020 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
L3: 00000140 0000001b0000010300000001010100000162000000010101800001290000000101010000016500000001010000000164000000010100000000010000000101010000000200000001010100000170000000010101000000030000000f014949494949494949494949494949490000010c000000010101000001040000000101010000010a000000010101000001060000000101010000010500000001010100000108000000010101000001070000000101018000012b000000010100000001610000000401000000100000000000000004010000000400000100000000040100000014800001030000000000000001020000000000000001100000000000000001110000000000800001280000000101000000016300000001010080000102000000100132303131313232383136323032313030000000000000000000000000
L4: 00000010 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
L5: 00000020 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx
L6: 00000020 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
L7: 00000020 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The parts which are 'x' are the data which looks encrypted in the original file and so I replaced them with 'x'. Line numbers, spaces and new-lines are also added by me to make the content more readable!
See chapter "Key Backup Feature Tutorial" in the document Key Management Utility User (KMU) Reference for overall scheme description.
Unfortunately this document has not been updated to the last version of the scheme which uses AES tK and HMAC for M_mK.
As far as I remember it is possible to tell KMU to use the older DES3 scheme with the -3 command line option.
I have a working implementation but unfortunately can not provide the code.
Key restoration steps summary:
Check overall file structure (magic 0x000001f4 | varLen encoded payload | 4 byte MAC | varLen wrapped MAC key | varLen wrapped transport key)
unwrap AES transport key (using the wrap key and it's key type specific algorithm, e.g. CKM_RSA_PKCS)
unwrap generic secret MAC key (using AES transport key and CKM_AES_ECB. Length is 32)
verify MAC of encoded payload (using MAC key with CKM_SHA512_HMAC_GENERAL)
unwrap all backed-up keys from the payload (using AES transport key with CKM_WRAPKEY_AES_CBC and zero IV)
You might want to use PKCS#11 logger library (see PTK-C Programming Guide) and record the activity of the KMU utility during key restore to verify the fine details of the algorithm.
Good luck with your project!
EDIT> Overall structure of file (given your example data):
000001f4 // Magic
000001a8 // Length of encoded payload
00000001 // Number of keys
000001a0 // Wrapped key #1 length
xxxx...xxxx // Wrapped key #1 data for CKM_WRAPKEY_AES_CBC
xxxxxxxx // Payload MAC
00000020 // Wrapped MAC key cryptogram length
xxxx...xxxx // Wrapped MAC key cryptogram
00000020 // Wrapped transport key cryptogram length
xxxx...xxxx // // Wrapped transport key cryptogram
See CKM_WRAPKEY_AES_CBC and CKM_WRAPKEY_DES3_CBC for format used to encode individual exported key data (#1..#n) inside encoded payload.

Why does Blockcypher signer tool return some extra characters than bip32 dart package?

I'm trying to sign a transaction skeleton Blockcypher returns, in order to send it along, following https://www.blockcypher.com/dev/bitcoin/#creating-transactions.
For this example, I'll use the completely-unsafe 'raw raw raw raw raw raw raw raw raw raw raw raw' mnemonic, which using dart bip32 package creates a BIP32 with private key 0x05a2716a8eb37eb2aaa72594573165349498aa6ca20c71346fb15d82c0cbbf7c and address mpQfiFFq7SHvzS9ebxMRGVohwHTRJJf9ra for BTC testnet.
Blockcypher Tx Skeleton tosign is 1cbbb4d229dcafe6dc3363daab8de99d6d38b043ce62b7129a8236e40053383e.
Using Blockcypher signer tool:
$ ./signer 1cbbb4d229dcafe6dc3363daab8de99d6d38b043ce62b7129a8236e40053383e 05a2716a8eb37eb2aaa72594573165349498aa6ca20c71346fb15d82c0cbbf7c
304402202711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc4022058f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab
On the other hand, using bip32 I get:
String toSign = txSkel['tosign'][0];
var uToSign = crypto.hexToBytes(toSign);
var signed = fromNode.sign(uToSign);
var signedHex = bufferToHex(signed);
var signedHexNo0x = signedHex.substring(2);
where fromNode is the bip32.BIP32 node. Output is signedHexNo0x = 2711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc458f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab.
At first sight, they seem completely different buffers, but after a detailed look, Blockcypher signer output only has some extra characters than that of bip32:
Blockcypher signer output (I split it into several lines for you to see it clearly):
30440220
2711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc4
0220
58f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab
bip32 output (also intentionally split):
2711792b72547d2a1730a319bd219854f0892451b8bc2ab8c17ec0c6cba4ecc4
58f675ca0af3db455913e59dadc7c5e0bd0bf1b8ef8c13e830a627a18ac375ab
I'd expect two 64-character numbers to give a 128-characters signature, which bip32 output accomplishes. Hence, Blockcypher signer output has 140 characters, i.e. 12 more than the former, which is clear when seen as split into lines as above.
I'd be really thankful to anyone throwing some light on this issue, which I need to understand and correct. I need to implement the solution in dart, I cannot use the signer script other than for testing.
The dart bip32 package doesn't seem to encode the signature in DER format, but rather in a simple (r, s) encoding. However DER is required for Bitcoin. For more information see:
https://bitcoin.stackexchange.com/questions/92680/what-are-the-der-signature-and-sec-format
You can either add the DER extra bytes yourself according to your r and s or check if there's a DER encoding in the dart bip32 library.

How to do SSL public key pinning in flutter/dart?

relatively new to Flutter here (and programming in general). Only familiar with the more basic stuffs but I've now encountered the need to use a CertificatePinner such as this in flutter/dart:
https://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html (I've successfully implemented this in my previous kotlin/java project in android studio). My goal is to pin public key (not certificate)
All I have is the public key in the form of a string like shown below, nothing else:
"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
How do I go about achieving this? I've asked this in an open issue on github but haven't gotten any responses yet (https://github.com/dart-lang/sdk/issues/35981). Hoping someone has managed to achieve this.
I've also scoured through other sources. I think the closest one to a solution for me is How can I do public key pinning in Flutter?
but I don't quite get what is being done there and I can't comment to ask questions there since I don't have enough reputation yet.
For comparison, all I want to do is achieve the same thing in flutter/dart what I could in java/kotlin with these few lines of code:
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
Thanks for your help
Start with the code in the answer you refer to. That takes the certificate in DER format and starts decoding it.
ASN1Parser p = ASN1Parser(der);
ASN1Sequence signedCert = p.nextObject() as ASN1Sequence;
ASN1Sequence cert = signedCert.elements[0] as ASN1Sequence;
ASN1Sequence pubKeyElement = cert.elements[6] as ASN1Sequence;
// this is the Subject Public Key element, which describes the type of key and actual value
For example, if we decode the certificate of pub.dev we find that it's an RSA key with a modulus of 65537 and a value of 2347......:
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
NULL
BIT STRING (1 elem)
SEQUENCE (2 elem)
INTEGER (2048 bit) 234782553149463204049153749736864715384123240676730175743244004248041…
INTEGER 65537
From the RFC, the SPKI fingerprint is the SHA-256 hash of this whole element.
// you need to import dart:convert and package:crypto/crypto.dart
var hash = base64.encode(sha256.convert(pubKeyElement.contentBytes()).bytes);
var spkiFingerprint = 'sha256/$hash'; // should match the value you have
Caveats
The badCertificateCallback doesn't deliver the whole certificate chain, so you can't walk up the whole chain. What's worse is that it doesn't always seem to deliver the leaf certificate! Sometimes it delivers an intermediate certificate.

MD5 file hash for the same unchanged file is different each time C#

Good evening all,
I've been working on an MD5 tool in C# that takes a file, goes through my Hasher class and pops the result in a database, along with the filename and directory.
The issue I'm having is that each time I run the test, the MD5 result for the same identical file i.e. unchanged in any way is completely different.
Below is the code I use
HashAlgorithm hmacMd5 = new HMACMD5();
byte[] hash;
try
{
using (Stream fileStream = new FileStream(fileLocation, FileMode.Open))
{
using (Stream bufferedStream = new BufferedStream(fileStream, 5600000))
{
hash = hmacMd5.ComputeHash(bufferedStream);
foreach (byte x in hash)
{
md5Result += x;
}
}
}
}
catch (UnauthorizedAccessException uae) { }
return md5Result;
Here are the results for 3 seperate runs of hello.mp2:
1401401571161052548110297623915056204169177
16724366215610475211823021169211793421
56154777074212779619017828183239971
Quite puzzling.
My only rational thought as to why I'm getting these results is with concatenating byte to string.
Can anyone spot an issue here?
Regards,
Ric
You should use System.Security.Cryptography.MD5 rather.
HMACMD5 doesn't compute a hash, it computes a message authentication code.
HMACMD5 is a type of keyed hash
algorithm that is constructed from the
MD5 hash function and used as a
Hash-based Message Authentication Code
(HMAC). The HMAC process mixes a
secret key with the message data,
hashes the result with the hash
function, mixes that hash value with
the secret key again, then applies the
hash function a second time. The
output hash will be 128 bits in length
Since you're not supplying the HMAC key, one is being generated randomly for you on your behalf and causing you to see different results.
My suggestion is that you are not computing MD5 hashes since MD5 produces a fixed length output of 32 hex numbers
Also, since you don't see any numbers from 0xA to 0xF is quite puzzling
You might wanna check a "real" result with online MD5 calculators such as this one
You shouldn't have a bufferedStream in between. I would guess a different number of bytes is buffered in each run.