I know there are already a few posts about this error, but I couldn't find an answer fitting my problem :
I created an AES key with the following command :
keytool -genseckey -alias TEST -keyalg AES -keysize 128 -storepass "a#b$c<d>"
-storetype JCEKS -keystore /usr/my/path/test.jck
I then try to access the keystore from java code :
String password = "a#b$c<d>";
char[] passwordChars= password.toCharArray();
// loading the file containing the key
InputStream inputStreamFichierCle;
try {
inputStreamFichierCle = new FileInputStream(filePath);
keyStore.load(inputStreamFichierCle, passwordChars);
}
And there I get an IOException : keystore was tampered with or password was incorrect.
Note that I tried with normal password (ex : pass) and this works perfectly, so I guess the problem here has to do with the special characters I use in my password.
What is happening, and how can I fix this?
The cause of this problem is the dollar sign in combination with bash command line.
Basically "$c" is substituted with the content of a variable with the name "c". Unfortunately there is no variable with this name, so it is replaced with an empty string.
You can avoid the variable substitution by using single quotes. See the difference:
$ echo "a#b$c<d>"
a#b<d>
$ echo 'a#b$c<d>'
a#b$c<d>
If you use the password "a#b<d>" in your java code, it will work.
Related
I'm trying to make a bash script which generates the same hash code like this java hash function:
import java.security.Security;
import org.apache.commons.codec.binary.Hex;
import java.io.UnsupportedEncodingException;
public final static String hash(final String _password)
throws Exception
{
String _salt="0c321e8669fce545";
Security.addProvider(new BouncyCastleProvider());
final String algorithm = "SHA-256";
final String encoding = "UTF-8";
MessageDigest md = MessageDigest.getInstance(algorithm);
md.reset();
md.update(_salt);
byte[] digest = md.digest(_password.getBytes(encoding));
char[] hashChars = Hex.encodeHex(digest);
return salt + new String(hashChars);
}
E.g. I tried it with a static salt and the ssl method to create the password hash:
#!/bin/bash
salt='0c321e8669fce545'
passwordHash=$(echo -n 'hello' | openssl sha256)
finalPassword=$(echo $salt$passwordHash)
But I never get the same result.
Is there any way to realize this?
Edit: My goal is to create a password which is accepted by a platform using this java function for it's password creation.
You need to use -salt and -pass parm. On CentOS, I do something like this in bash:
############################################
# To be used with the -pass parm of openssl
# It's the salt for creating hash
############################################
export enc_salt="0c321e8669fce545"
encyption_file="/tmp/encrypted_pwd.enc"
echo -n 'hello' | openssl aes-256-cbc -md sha256 -a -salt -pass env:enc_salt -out $encyption_file
# Decrypt
openssl aes-256-cbc -md sha256 -d -a -salt -pass env:enc_salt -in $encyption_file
Since each time you run this, even with the same salt, it will give you a different hash. I doubt it will ever match what you are seeing in your Java program. What's important is when you de-crypt the encryption in bash or Java, it should give you what you are looking for.
The reason for using an environment variable for the salt is so anyone lurking using ps command monitoring what is executing will not see the salt passed to openssl
On a 64 bit OS, substitute -base64 for -a parm when using openssl
I am having some trouble with XML digital signature with a PFX file. I get an exception when I run this code:
KeyStore ks = KeyStore.getInstance("PKCS12");
fs = new FileInputStream(file);
ks.load(fs, "password".toCharArray());
// this line!
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("alias", new KeyStore.PasswordProtection("password".toCharArray()));
This is the exception:
java.security.UnrecoverableKeyException: Get Key failed:
java.security.InvalidKeyException: Invalid RSA private key
at sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:435)
at sun.security.pkcs12.PKCS12KeyStore.engineGetEntry(PKCS12KeyStore.java:1306)
at java.security.KeyStore.getEntry(KeyStore.java:1521)
at app.ubl.xml.GenerateSignature.makeSignatureXML(GenerateSignature.java:88)
Caused by: java.io.IOException: DerInputStream.getLength(): Redundant length bytes found
at sun.security.util.DerInputStream.getLength(DerInputStream.java:606)
at sun.security.util.DerInputStream.getLength(DerInputStream.java:569)
at sun.security.util.DerInputStream.getPositiveBigInteger(DerInputStream.java:220)
at sun.security.rsa.RSAPrivateCrtKeyImpl.parseKeyBits(RSAPrivateCrtKeyImpl.java:205)
The real problem is that the code works in java 1.8.0_111, but any higher version the error shows.
Exception when running keytool
Another issue that I found is that when I run this command using keytool:
keytool -list -keystore file.pfx -storepass password -storetype PKCS12 -v
to show the details of the PFX file then again it only works on java 1.8.0_111. Otherwise I get this exception:
java.util.IllegalFormatConversionException: d != java.lang.String
at java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4302)
at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2793)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:2747)
at java.util.Formatter.format(Formatter.java:2520)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2940)
at sun.security.tools.keytool.Main.withWeak(Main.java:3076)
at sun.security.tools.keytool.Main.printX509Cert(Main.java:3125)
at sun.security.tools.keytool.Main.doPrintEntry(Main.java:1924)
at sun.security.tools.keytool.Main.doPrintEntries(Main.java:2236)
at sun.security.tools.keytool.Main.doCommands(Main.java:1123)
at sun.security.tools.keytool.Main.run(Main.java:366)
at sun.security.tools.keytool.Main.main(Main.java:359)
I don't know if this is relevant, but that is all I got.
Any thoughts?
PD: Sorry for my bad english.
Stricter DER checking in 8u121
This is a change that was introduced in the Java versions after Java 8u111. The next version was Java 8u121.
As a result some encodings in the underlying ASN.1 format are no longer being accepted.
And this change is mentioned in the 8u121 release notes as such:
security-libs
More checks added to DER encoding parsing code
More checks are added to the DER encoding parsing code to catch various encoding errors. In addition, signatures which contain constructed indefinite length encoding will now lead to IOException during parsing. Note that signatures generated using JDK default providers are not affected by this change.
JDK-8168714 (not public)
Reason: CVE
This specific change was because of a vulnerability: CVE-2016-5546.
Relevant source code change
The error messages you saw (Redundant length bytes found) was introduced as part this changeset. The summary was this:
8168714: Tighten ECDSA validation
Summary: Added additional checks to DER parsing code
Unfortunately as of now (2018) the bug mentioned in the changeset "Bug 8168714" is STILL marked private. (You won't be able to view it without a login.)
But here's another bug that is public: OpenJDK: JDK-8175251:
Failed to load RSA private key from pkcs12. (A less complete version of this bug is also on java.com.)
And this bug links to the source code changeset mentioned above.
Workaround: Run through OpenSSL washing machine to clean up P12 file
The encoding in some P12 files seems to be broken somehow. A workaround is to unwrap the P12's contents to PEM with OpenSSL and then rewrap them into a new P12 file. (The real solution would be to fix/update the software that generates the P12 file.)
The following command is mentioned in the Java Bug ticket:
Openssl is able to remove the redundant 0s when extracting the private key. We can use the following 2 commands to normalize an affected pkcs12 file:
openssl pkcs12 -in pkcs12-file -out key-and-cert -nodes -passin pass:abcXYZ
openssl pkcs12 -in key-and-cert -export -out new-pkcs12-file -passout pass:abcXYZ
Further reading: Bouncy Castle Mailing List
Update 2018-02-12Mon: I just realized that the issue discussed on the Bouncy Castle mailing list deals with a DIFFERENT type of encoding error. Namely: in ASN.1 you have T-L-V (Tag-Length-Value) encoding.
And OP's question had to do with a non-standard/faulty encoding of the LENGTH attribute. (Error message: Redundant length bytes found.)
While the issue discussed on the BC-ML had to do with a non-standard/faulty encoding of the VALUE attribute. (Error message: Invalid encoding: redundant leading 0s.)
But both of these issues are related to unnecessarily (and therefore illegally) padding with 0x00.
This change was discussed on the mailing list for the Java "Bouncy Castle" cryptography library:
dev-crypto#bouncycastle.org mailing list, 2017-06-27, Strict Public Key checking leads to broken certificates (Archived here. Official archive here.)
And as a result a special configuration parameter (org.bouncycastle.asn1.allow_unsafe_integer) was introduced in BC version 1.58.
(See release notes section 2.2.3.)
Related questions
2017-06-27, SO: Unable to open keystore in AndroidStudio - "Redundant length bytes found"
2017-03-17, SO: Signing android app throws IOException: Redundant length bytes found
To work-around this issue, you can re-encode the signature bytes using the ASN1 stuff from Bouncycastle.
BigInteger r, s;
// This works because bouncycastle isn't picky like the updated JCE is.
try(ASN1InputStream is = new ASN1InputStream(origSigBytes)) {
ASN1Sequence seq = (ASN1Sequence)is.readObject();
ASN1Integer r_int = (ASN1Integer)seq.getObjectAt(0);
ASN1Integer s_int = (ASN1Integer)seq.getObjectAt(1);
r = r_int.getValue();
s = s_int.getValue();
}
DERSequence out = new DERSequence(new ASN1Encodable[] {new DERInteger(r), new DERInteger(s)});
byte[] sigBytes = out.getEncoded();
This works if you're in a context where you have the signature as a byte[] and are about to call validate().
add BouncyCastleProvider provider to java.security.Security
Security.addProvider(new BouncyCastleProvider());
and it will work
The doc states:
"You will always need this password in order to access the keystore entry containing that key."
http://docs.oracle.com/javase/tutorial/security/toolsign/step3.html
But whatever keypasswd I set I can export the certificate without it, and the resulting file are all binary equal.
Why don't we need to specify the keypasswd to export the certificate?
The exported certificate is a PUBLIC KEY so it doesn't require password (Is not meant to be protected) what you actually protect is a KEYSTORE (Combination of public + private keys) that's why keytool doesn't require password for export public keys...
I have a Java keystore file using the storetype JCEKS. It contains SecretKeyEntry entries. I would like to be able to dump, from the command line, the actual secret keys stored in this file. I tried this:
keytool -list -keystore secretkeys.jks -storetype JCEKS
which returned
Keystore type: JCEKS
Keystore provider: SunJCE
Your keystore contains 1 entry
secret1, May 27, 2016, SecretKeyEntry
But that does not show me the key itself. How can I extract and look at, from the command line, the secret key?
This is not possible with keytool.
Converting the keystore to PKCS#12 and then using OpenSSL to view the key doesn't work either, because this is a symmetric key (SecretKeyEntry).
If you are stuck with the command line, you could write a small Java program that does it. Something like this:
String fileName = "secretkey.ks";
char[] password = "mypassword".toCharArray();
String alias = "secret1";
KeyStore ks = KeyStore.getInstance("JCEKS");
try (FileInputStream fis = new FileInputStream(fileName)) {
ks.load(fis, password);
SecretKey secretKey = (SecretKey) ks.getKey(alias, password);
System.out.println(new BigInteger(1, secretKey.getEncoded()).toString(16));
}
This prints out the secret key as a hex string (toString() with radix 16).
Or you could use the GUI program KeyStore Explorer.
I extracted a Key and its certification chain from a JKS, and now I'm trying to add this key to the Windows Keystore using Java.
To load my JKS I did the following:
String jksPath = "D:\\mykeystore.jks";
KeyStore keystore = KeyStore.getInstance("JKS");
FileInputStream fIn = new FileInputStream(jksPath);
keystore.load(fIn, "12345678".toCharArray());
Then I get the key and the certification chain:
Key key = keystore.getKey("res1", "12345678".toCharArray());
Certificate[] cchain = keystore.getCertificateChain("res1");
So far so good, then I try to add this key to my Windows Keystore:
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
ks.setKeyEntry("myKey", key, "12345678".toCharArray(), cchain);
And BOOM:
Exception in thread "main" java.lang.ClassCastException: [Ljava.security.cert.Certificate; cannot be cast to [Ljava.security.cert.X509Certificate;
at sun.security.mscapi.KeyStore.engineSetKeyEntry(KeyStore.java:402)
at sun.security.mscapi.KeyStore$MY.engineSetKeyEntry(KeyStore.java:62)
at java.security.KeyStore.setKeyEntry(KeyStore.java:909)
Exception thrown due to the setKeyEntry call.
P.S: when I use the same syntaxe on a JKS type of Keystore no exception is thrown.
It seems there's a clumsy bit of java code in the implementation of sun.security.mscapi.KeyStore.engineSetKeyEntry(). It tries to convert an array of Certificates ("[Ljava.security.cert.Certificate", notice the prefix in the class name) to an array of X509Certificates (" [Ljava.security.cert.X509Certificate"), which is not something java ever allows you to do with a simple cast expression (e.g. see discussion of a similar mistake).
All I did in a similar situation is to pass the certificate array to the keyStore.setKeyEntry() method call as a X509Certificate array, rather than a simple Certificate array.