Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
482 views
in Technique[技术] by (71.8m points)

java - Unable to convert .jks to .pkcs12: excess private key

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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); }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...