We’re using a Java libarary called license3j for license management. The library uses asymmetric encryption and relies itself on Bouncycastle. We create a license file using a simple gpg command and verify the license within our software using our public key. So far everything worked fine. BUT: In 1,000 generated licenses, there is a very small fraction which cannot be verified correctly, although, they are in fact valid (approximately 5/1000).
What happens in this case: When the license is to be verified in com.verhas.licensor.License.setLicenseEncoded(InputStream), the org.bouncycastle.openpgp.PGPOnePassSignature.verify(PGPSignature) throws the following exception:
org.bouncycastle.openpgp.PGPRuntimeOperationException: unable to verify signature: Signature length not correct: got 511 but was expecting 512
Sounds rather obscure to me, having only basic cryptography knowledge. Spending hours googling, gave me the clue, that there is something about "leading zeros". So, in the given example, obviously a leading zero was stripped away somewhere (where?), and the lengths of the signature data to compare do not match. Makes sense.
Now, I’ve no clue, where the issue might be located. Is it during creation of the license file? Essentially, we’re just doing the following:
gpg --armor --local-user=name.of.software --sign
Which will give us the license file.
Or does the error happen during verification? Do I have to modify any Bouncycastle configuration to correctly address the leading zero issue? Their FAQ gives some hints, but the License3j source obviously never makes use of any Cipher instance, so I’m totally lost on how to integrate this into the given API.
I’m aware that this is a very special problem with a library which is obviously not very well known. Thus, I appreciate any little feedback or input.
Looks like it is a bug in bouncycastle, only encountered in versions of Java after 1.6, bouncycastle has always created the data wrong, but Java became more strict since 1.7 in the data it will accept during verification.
Bouncycastle is failing to pad the signature to the right length when it serializes it to the file, if the integer has enough leading zeros then the byte representation of it will be smaller.
Java versions 1.7 and above expect RSA signature bytes to be the same length as the key.
Bouncycastle converts the RSA signature byte array(returned from Java's RSA JCE provider) into an integer and it discards information about its length.
Line 263 of the PGPSignatureGenerator shows where the RSA signature bytes are returned from JCE and converted into an integer.
This integer is eventually written to the outputstream using MPInteger#encode which just uses the bitlength of the underlying biginteger to determine how much data to write.
This answer describes more about why you see this aproximatley one in every 200 cases and how the version of Java plays a role.
Related
I'm thinking how to protect the JAR from reverse-engineering.
(Question 1):
If JAR is signed by signature, is the signature able to protect the JAR? If not, then what is the purpose of signing?
(Question 2):
Android APK can be signed by platform signature, and It is impossible for extracting the platform signature from the APK itself.
From this point of view, I think signature can be used to protect something. But why can we decompile any APK even it is protected by platform signature?
Think about your own signature, that you write with a pen. Does signing a piece of paper make the contents of the paper somehow impossible to read?
No: it simply gives somebody reading the paper an indication that it was written by you (or you agree with its contents, or whatever).
And so it is with signing a jar: all it does is gives people an indication that you (or people you have authorized to use the signature) produced it, in order that nobody else can change the jar contents and present it as "authentic".
Signatures will not help you to stop reverse engineering.
If JAR is signed by signature, is the signature able to protect the JAR? If not, then what is the purpose of signing?
If you sign a contract, does that prevent any of the involved parties from breaking the contract?! Nope, it doe not. The signature is simply something that (theoretically) legally identifies you.
And that is the whole point of signing software: to tell users of the software: "this is my delivery, you can trust it, because I signed it (and nobody else can have changed it)".
Signatures do nothing to protect your content from reverse engineering. They are solely useful for users of your content, as they can be "sure", that they are really using your content, and not something that a third party faked and only claims to be your delivery.
The only kind of protection is to look into obfuscating your JAR file content. There are tools for that (especially on Android). Nonetheless obfuscation for Java isn't exactly perfect. See here for example.
If JAR is signed by signature, is the signature able to protect the JAR? If not, then what is the purpose of signing?
As the others have pointed out, it prevents others from distributing their own copies of your code under your name/brand. For example, suppose you write a calculator and post a signed bytecode on github, then I can't write my own bytecode and claim (without getting caught that its a false one, unless I find the key with which it was signed, which itself is very very ... very unlikely) that it is a copy of your calculator.
But why can we decompile any APK even it is protected by platform signature?
Because, it does not prevent us from decompilation. We can get the source code from such APKs but, we cannot like put our own arbitrary code in its place and makes others believe it is same as yours.
Put in plain terms, a signature is a proof of source and authenticity of a code, nothing more.
Edit:
Why can't we extract the original signature from the APK? Why is it impossible?
Because the signature is a hash, generated using a cryptographic hash function and a key that only you hold (called private key) and a public key, which is freely available. Since that private key is with you and assuming that you used a powerful cryptographic hash function, it will take a lot of time for a person trying to impersonate that key to find that key by directly trying all the hashes possible, which is usually the only possible way unless you have a clever work-around to find the key. The public key is openly available and is used for verification of the authenticity of the signature.
Can we use this mechanism for protecting our JAR?
Depends on what you are trying to protect. If it is impersonation by somebody else, then yes, for sometime, as long as they do not stumble upon the key by some way. As seems for obvious, it is no good against reverse engineering
I am developing a digital signature java class using the SHA1withRSA algorithm with a 256 bits certificate. But I am getting this error:
Signature length not correct: got 344 but was expecting 256.
I am using the SunJSSE provider (that not implements the SHA256withRSA algorithm) because he uses the PKCS12 keystore type. And I need this, because this is a batch application.
Is the SAH1withRSA algorithm incompatible with 256 bits certificate?
First, you can't possibly have a 256-bit RSA certificate, or key. 256-bit RSA wasn't secure when RSA was published in 1977 much less anytime close to now. That exception says it is looking for a 256 byte signature, which is 2048 bits, implying a key and certificate also of 2048 bits, which is currently (since 2015) the standard and widely used size for RSA.
Second, yes SHA1withRSA technically works with a 2048-bit RSA key (and certificate), although it is less secure. SHA1 was originally rated for only 80-bit strength against collision, and has been broken (at significantly lower cost, about 63 bits) about a year ago -- see https://shattered.io -- making it insecure for signatures in many cases, and thus leading more and more systems, programs, and users to reject SHA1 signatures. For example, all Oracle Java packages from 8u144 up (roughly last summer) have java.security jdk.certpath.disabledAlgorithms configured by default to prohibit use of SHA1-signed certificates in TLS (including SSL, but SSL is also broken and prohibited by default). Java doesn't currently prohibit other uses of SHA1 signatures, but may be changed to. And lots and lots of things you might want to communicate or interoperate with, like browsers, webservers, email systems, repositories, etc, either have already prohibited SHA1 or probably will.
But it isn't necessary. You can use multiple providers in one program, and in particular you can use the KeyStore PKCS12 from SunJSSE while also using the Signature SHA256withRSA (or other SHA2+RSA variants) from SunRsaSign. In fact it's easiest to not specify the provider(s) at all and just let KeyStore.getInstance() and Signature.getInstance() (and the other JCA interface classes) find the correct provider automatically.
Finally, your signature is probably base64-encoded: ceil(256/3)*4=344. Take a look at the data and see if it consists of base64 characters, and if so which set. If it is base64, decode it to binary (i.e. byte[] in Java) and use that. In Java8+ just use java.util.Base64 which supports both variants now common: the 'MIME' variant and the 'URL-safe' (primarily JSON) variant. In older Java you can use javax.xml.bind.DatatypeConverter for MIME, or any number of third-party libraries with varying capabilities.
I need to decrypt messages via RSA in order to send it over an unsecured channel, but I'm afraid of the Padding Oracle Attack. Therefore I already have asked the follwoing questions:
How to verify the integrity of RSA encrypted messages?
How to ensure message integrity for RSA ciphers by using javax.crypto.Cipher
Like suggested in the first question,
However, since you are using a high level cryptographic library, this is something you shouldn't have to worry about. The writers of that library should have taken care of it.
I shouldn't consider about. As far I know, the RSA implementation of PKCS#1 v1.5 is vulnerable to the Padding Oracale Attack whereby OAEP isn't (assumed it's implemented correctly)
Hence I want to know which padding implementation is used by javax.crypt.Cipher by Java 7
It depends on the chosen or default provider which padding is actually used when you instantiate a Cipher without fully qualifying it like:
Cipher.getInstance("RSA")
Doing so is a bad practice, because if you switch Java implementations, there might be different defaults and suddenly, you won't be compatible with the old ciphertexts anymore. Always fully qualify the cipher.
As I said before, the default will probably (there are many providers, one can't be sure) be PKCS#1 v1.5 padding. If you need another, you would have to specify it. If you want to use OAEP, here is a fully qualified cipher string from here:
Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
That's not a good advice given in the first link to the cryptography site. You should never rely on the defaults of cryptographic libraries cryptographic algorithms. There are quite a few reasons for this:
Different implementations, different defaults (there are no requirements for cryptography providers concerning defaults, although most will copy the Oracle/Sun defaults);
What's secure now may not be considered secure tomorrow, and because for backwards compatibility, you can never change the default;
It's unclear to anybody reading your software what the default is (you could document it, but in that case you might as well write it out).
The SunJCEProvider provided by Oracle defaults to PKCS#1 padding ("PKCS1Padding") for historical reasons (see reason #2 above). This is not well documented.
At that time that default was set you basically had just the insecure textbook RSA ("NoPadding") and the PKCS#1 v1.5 version ("PKCS1Padding" or RSAES-PKCS1-v1_5 in the PKCS#1 v2.1 standard). At that time RSAES-PKCS1-v1_5 was definitely the more secure choice. Changing the default now to OAEP would break every RSA implementation out there that uses the default.
The advice of otus (in the first link within this answer) is be better suited to protocol implementations in libraries than to cryptographic algorithms. In the end you should be able to defend the security of the choices made, whatever you choose.
The default for bouncy-castle when you just specify RSA is RSA/NONE/NOPADDING
This is the same result as RSA/ECB/NOPADDING as well.
I've been told to encrypt some form data (strings) using AES256 and was given a nice library which already does it all. I was just trying to make sure I understand it a bit better and learn a bit about encryption since it isn't something I am ever comfortable with. While doing that I ran a test I saw on some website, it said to call this Cipher.getMaxAllowedKeyLength("AES") which gives you the maximum key length. The result was 128.
Anyways the max allowed key length is 128, does that mean I can not use AES256? or are those unrelated?
EDIT: I should mention that I do know how to get the unlimited policy files to change this, I'm just trying to understand this whole deal better before proceeding.
For US export restriction reasons, Java ships with 128-bit security by default only. You need to download and install the Java Cryptography Extension if you want to work with 256-bit+ security.
The getMaxAllowedKeyLength() has been introduced just for this purpose, otherwise you would have to handle an exception during the Cipher encryption/decryption operations (update and doFinal) to test if the restrictions apply. As the policy files may change in time or for different versions of Java, it is easier to test with a method.
Note that getMaxAllowedKeyLength() should not be used for any other reason than testing for restrictions. Notably, it may well return Integer.MAX_VALUE instead of a valid key size.
And of course, if it returns 128 you cannot use AES with a key size of 256.
To remedy this you need to install the Unlimited Strength Jurisdiction Policy Files for the Oracle JRE / JDK and then copy it into the (jre)/lib/security folder of all the Java installations where you want to use larger key sizes. You can overwrite the files that are already there. You may need local admin rights or similar rights on that folder to do so.
If that is not possible you could use another implementation of AES that doesn't require the Cipher class as this class actually enforces the limitations. There are a few tricks around this issue as well.
I'm trying to make a decryption of a string in JAVA encrypted in Delphi with DCPcrypt. DCPcrypt uses a Hash algorithms and a custom key for initialization, then an encryption algorithm. Is it possible to decrypt it in JAVA using Java ™ Cryptography Architecture or another JAVA cryptography library? If not have you any idea what libraries combination in JAVA and Delphi allows to have the same results for decryption and encryption of any string ?
Try Chilkat Android and Chilkat Delphi: https://www.chilkatsoft.com
The one thing you have to do to get the Chilkat examples for Delphi and Android to produce the exact same encrypted strings is change the padding scheme to 4. Out of the box, the Android example has a padding scheme of 0, and that results in different trailing bytes. It's worth noting that the Delphi and Java examples have no issues at all.
Now that you've got the answer, and having spent a week on this, I can tell you what doesn't work.
Trying to mix 3DES messages, whether using SHA1 or MD5 on the password, between JCE+JCA and Delphi Encryption Compendium is not going to work. For starters, DEC MD5 is crap. You're better off using the Indy MD5 hash class. It's too bad the version of Indy that comes with XE doesn't include encryption, because their MD5 hash class is flawless and easy to use.
Lockbox didn't want to play nice with Java encryption. In that case it may be that I was so burned out on DEC that I had lost all patience. Again, I spent an entire week, all day and into the night trying to get the various parts to work. I never got to DCP. The next thing I tried was Chilkat.
With Chilkat, I downloaded the trials and installed them, ran the examples, and it all worked straight out of the box. Done.