3

Update Dec 30, 2017 – 4:

I've managed to unpack .jks file and extract key and certs from it. To do this I wrote a small nodejs program inspired by signerbox2 an open-source project that uses .jks to sign data. In particular, I use jksreader npm package, which only exists for several days now!

The program I wrote looks like this:

const fs = require('fs');
      jksreader = require('jksreader'),

      pathToFile = process.argv[2],
      password = process.argv[3],

      contents = fs.readFileSync(pathToFile),
      parsedContent = jksreader.parse(contents);

var   key = jksreader.decode(parsedContent.material[0].key, password);

fs.writeFileSync('key', key);

for (var i = 0; i < parsedContent.material[0].certs.length; i++) {
  var cert = parsedContent.material[0].certs[i];
  fs.writeFileSync('cert' + i, cert);
}

This program is invoked like this:

node index.js /path/tp/my_key.jks my_password

The output looks like a bunch of files:

cert0
cert1
cert2
cert3
key

The certs are in DER format, and can be read like this (note the -engine dstu parameter):

openssl x509 -in cert2 -inform der -text -noout -engine dstu

However, I can't figure out how to read (or convert to PEM) the key. I'm still working on it. The openssl asn1parse works well on the key file. Here's the openssl asn1parse output. I'm not sure where to go from here.

Update Dec 28, 2017 – 3:

I installed Keystore Explorer. It cannot extract the private key either. It can show a bit more info than I was able to get with keytool. The only weird thing here is that there are two almost identical copies of the same certificate, named after my name (in caps):

enter image description here

Here's the indication that the entry follows DSTU-4145 standard:

enter image description here

Update Dec 28, 2017 – 2:

The key contained in .jks follows or somehow related to "DSTU-4145 signature scheme" (algorithm). This is a government standard in Ukraine.

I barely know anything about signature schemes; DSTU-4145 can be seed on the BouncyCastle specification page, for instance.

Maybe I somehow need to install the DSTU-4145 algorithms, so that keytool knows how to extract the private key?

Update Dec 28, 2017 – 1:

Doing this under the following version of java on Ubuntu:

$ java -version
java version "1.7.0_151"
OpenJDK Runtime Environment (IcedTea 2.6.11) (7u151-2.6.11-2ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.151-b01, mixed mode)

Also does not work on the following version of Java under macOS:

java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

I'm provided with a .jks from an official authority.

I'd like to convert .jks file into .pkcs12 to use it with openssl to actually sign something. Instead, I get an error.

This is what I'm doing:

$ keytool -importkeystore \
        -srckeystore my_keystore.jks \
        -destkeystore my_keystore.pkcs12 \
        -deststoretype pkcs12

Enter destination keystore password:
Re-enter new password:
Enter source keystore password:
Enter key password for <my_key>
keytool error: java.security.UnrecoverableKeyException: excess private key

At the same time, however, the key can be listed:

$ keytool -list -keystore my_key.jks
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

my_key, Jan 18, 1970, PrivateKeyEntry,
Certificate fingerprint (SHA1): A1:B2:C3:D4:E5:F6:85:E4:2B:03:B9:68:FD:AE:9D:5B:24:CF:BF:FF

What am I doing wrong?

oldhomemovie
  • 14,621
  • 13
  • 64
  • 99
  • can you check if you don't have the key twice in your keystore? I think you have it already imported and that gives you the `excess private key` – Andrei Sfat Dec 28 '17 at 21:18
  • @sfat check how exactly? You mean I might have accidentally created `my_key.pkcs12`? No, I just checked, there's no such file anywhere – oldhomemovie Dec 28 '17 at 21:27
  • (1) `keytool -list` with `-v` should display the full chain -- and that duplicated name is indeed odd -- but won't help with privatekey (2) code _using_ the key needs a provider like BC but keytool should not (3) that error message is in `sun.security.pkcs.PKCS8Key.parseKey` and suggests a bug in the writing system, but I see no way to isolate it from `sun.security.provider.KeyProtector` which makes confirming this very difficult. Does the authority not offer the key (and cert) in any other form? Do they offer any test or dummy key you could safely make public? – dave_thompson_085 Dec 29 '17 at 11:08
  • 1
    from what I see people having the same issue usually would bump into the situation that the keystore has duplicate entry or because the password used for keystore and keypassword are not matching and should be the same. I am not sure about your settings if you have different passwords or you used the same password. Also, have you installed JCE (http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html)? It's pretty difficult to figure out these kind of problems since there are a lot of stuff that cannot be disclosed because of obvious reasons :) – Andrei Sfat Dec 29 '17 at 13:28
  • @sfat @dave_thompson_085 thank you guys for thoughtful comments! I've managed to unpack `.jks` file: I have the certs and key all as separate files. Now I'm struggling to determine the format the key is in, to be able to actually use it. – oldhomemovie Dec 30 '17 at 15:12

1 Answers1

2

Good work finding that JS. (FYI Stack only does one @ per comment -- fortunately I saw this in recently-modified.)

From that we can now see your privatekey has attributes in the PKCS8 (a legal but very rarely used feature) which on rechecking I see standard JCE does not support (there's a comment in decode but not in parseKey). It appears BouncyCastle does -- but bcprov doesn't do JKS.

I suggest you proceed with OpenSSL here since that's what you want to end up with anyway. You can definitely convert certificate file from binary/DER to PEM with:

 openssl x509 -in cert$i -inform der -out cert$i.pem -engine dstu 
 # or maybe pem$i or whatever names you find convenient

I'm pretty sure you can convert the key file to PEM with:

 openssl pkey -in key -inform der -out key.pem -engine dstu
 # or whatever name
 # output defaults to unencrypted but your input is already unencrypted so no loss

but if that doesn't work you can do it by hand:

(echo "-----BEGIN PRIVATE KEY-----"; openssl base64 <key; echo "-----END PRIVATE KEY-----") >key.pem 

and then you should be able to feed the PEM files to openssl pkcs12 -export -engine dstu to create a PKCS12.

Altternatively, since StackOverflow is supposed to be about programming, I think it will work if you use the BC provider to read the key and write a PKCS12, something like:

KeyFactory kf = KeyFactory.getInstance ("DSTU4145", "BC");
PrivateKey key = kf.generatePrivate (new PKCS8EncodedKeySpec (Files.readAllBytes("key")));
CertificateFactory cf = CertificateFactory.getInstance ("X.509", "BC");
// not sure we need the BC implementation for the certs, but can't hurt 
Certificate[] certs = new Certificate[4];
for( int i = 0; i < 4; i++ ){
  certs[i] = cf.generateCertificate (new FileInputStream ("cert"+i));
  // or "cert"+i+".pem" -- CertificateFactory can read either DER or PEM
}
// or concatenate the cert PEM files into one and do
Certificate[] certs = cf.generateCertificates (new FileInputStream ("certs.pem")).toArray(new Certificate[0]);
// better to close the stream(s) when done like below but for input isn't vital

KeyStore ks = KeyStore.getInstance ("PKCS12", "BC"); ks.load(null); 
char[] pw = "password".toCharArray(); // or preferably better value
ks.setKeyEntry("mykey", pkey, pw, certs);
try(OutputStream os = new FileOutputStream("result")){ ks.store(os, pw); }
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • Hi Dave, this is awesome, thank you! I'm going to accept the answer. A couple of questions: how exactly did you figure out this the key is in PKCS8? I don't have any idea how to run Java programs + install/include dependent libraries (e.g. BouncyCastle). I'll start figuring this out right now, but maybe you could help me out a little and post a full program listing please? – oldhomemovie Jan 06 '18 at 09:50
  • I guess I'll start with examples in https://www.bouncycastle.org/fips-java/BCFipsIn100.pdf and this sample program listing. Both seem like good sources. – oldhomemovie Jan 06 '18 at 09:57
  • @gmile: I know that JKS format uses PKCS8-encrypted for private keys from having worked with it in the past; I guessed that something described as a 'reader' would keep PKCS8 format, and the asn1parse output you provided confirmed it. Unless you need FIPS compliance for some reason -- and I expect someone using Ukrainian data is probably not part of the US government -- I would recommend NOT limiting yourself to the FIPS mode of Bouncy, or anything else, since it is likely to force extra effort to work around its limitations that doesn't actually benefit you. ... – dave_thompson_085 Jan 07 '18 at 00:52
  • ... The Bouncy cryptoprovider works the same way as other Java cryptoproviders -- this is an intended and important feature of Java Crypto Architecture -- so you can just use standard Java's documentation (aka javadoc) for `KeyFactory CertificateFactory` and so on, _except_ you pass "BC" as the provider argument in some calls (as I showed) and have the bcprov jar in your JVM when running. If you have a general question about Java programming, that probably should be a different Q -- and may well already be answered, since SO has _many_ Q&As about Java programming. – dave_thompson_085 Jan 07 '18 at 00:59