/* Copyright 2009 Albrecht Weinert, Bochum, Germany (a-weinert.de)
* All rights reserved.
*
* This file is part of Frame4J
* ( frame4j.de https://weinert-automation.de/software/frame4j/ )
*
* Frame4J is made available under the terms of the
* Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/ or as text in
https://weinert-automation.de/java/docs/frame4j/de/frame4j/doc-files/epl.txt
* within the source distribution
*/
package de.frame4j;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import de.frame4j.graf.AskDialog;
import de.frame4j.util.App;
import de.frame4j.util.AppBase;
import de.frame4j.util.MinDoc;
import de.frame4j.text.TextHelper;
//import sun.misc.BASE64Encoder;
import java.util.Base64;
/** Determine / extract public key from keystore or certificate.
*
* This Java application extracts a public key from a
* - certificate (that will be a .cer file mostly)
* or from a
* - so called keystore (mostly file based and mostly of type
* "jks" = java keystore).
*
* A password question may be necessary. If so it will be done graphically
* using an {@link AskDialog AskDialog} window. The user's password entry
* will not be visible / readable.
*
* The public key's output will be in textual form (human readable) to the
* console and optionally to a (log) file (that is to {@link App#log}).
* Additionally a binary public key file may be output, from which the key
* can be read / regenerated by standard Java means.
*
* Hint: To this application {@link PKextr}
* (source) belongs
* a .properties file
* PKextr.properties
* that may be considered as part of the documentation and may
* answer open questions. Get help by
* java de.frame4j.PKextr -? [-en | -de ]
*
* Hint 2: Provided one has the necessary privileges this application can
* also extract the private key. by e.g.
* java de.frame4j.PKextr -exportPrivate -pkcs12 -ks leFile.jks alias
*
*
* Copyright 2004 - 2008, 2016 Albrecht Weinert
*
* @see App
*/
// so far V00.00 (27.12.2004) : new
// V02.02 (09.02.2005) : /**, AskDialog addition
// V02.10 (07.11.2008) : can also extract the private key
// V.011+ (03.02.2010) : left Kenai due to Oracle-Sun kill
// V.133+ (24,06.2016) : ex sun.misc.BASE64Encoder & refine
// V.163+ (05.08.2016) : refactored to Frame4J'89 slimline
@MinDoc(
copyright = "Copyright 2004 - 2009, 2016 A. Weinert",
author = "Albrecht Weinert",
version = "V.$Revision: 44 $",
lastModified = "$Date: 2021-05-06 19:43:45 +0200 (Do, 06 Mai 2021) $",
usage = "start as Java application (-? for help)",
purpose = "extract keys from keystores or certificates"
) public class PKextr extends App {
private PKextr(){} // no objects; no javaDoc
/** Output file for the public key.
*/
protected String pubKeyFil; // word0 +
/** Type of certificate / keystore.
*
* Allowed values for certificate: "X.509"
* Allowed values for keystore: "JKS",
* "PKCS12"
* default: "X.509"
*/
public String certType = "X.509"; // "PKCS12", "JKS"
/** Read from certificate not from keystore.
*
* default: true
*
* @see #certType
*/
public boolean fromCert = true;
/** Export the private instead of the public key.
*
* default: false
*/
public boolean exportPrivate;
/** Name of input file (certificate or keystore).
*
* default: null
*/
public String certFileName; // behind option parameter -ks
/** Password for the keystore. */
public String storePassW;
/** Alias name for key from keystore.
*/
protected String keyAlias; // word 0
/** Word parameter 1 and 2. */
public String w0, w1;
/** Start method of PKextr.
*
* The application end with exit-code 0 on success. Exit-code > 0 means
* abort due to a problem.
*
* @param args command line parameter
* Execute: PKextr [options]
*/
public static void main(final String[] args){
try {
new PKextr().go(args);
} catch (Exception e) {
AppBase.exit(e, INIT_ERROR);
}
} // main(String[])
/** The fetched / extracted public key.
*/
PublicKey pubKey;
FileInputStream fis;
CertificateFactory cFac;
Certificate cert;
KeyStore ks;
FileOutputStream keyfos;
char[] passWord;
/** Working method of PKextr.
*
* @return > 0: error; 0: OK
*/
@Override public int doIt(){
log.println();
log.println(twoLineStartMsg().append('\n'));
if (isTest()) {
log.println(prop.toString());
} // >= TEST
try { // open input
if (fromCert) {
certFileName = w0;
} else if (certFileName == null) {
certFileName = System.getProperty("user.home") + "/.keystore";
}
fis = new FileInputStream(certFileName);
} catch (FileNotFoundException e) {
if (isTest()) {
log.println(formMessage("openexcp", certFileName));
e.printStackTrace(log);
}
return errMeld(17, formMessage("openexcp", e));
} // open input
if (fromCert) { // cert (else keystore)
pubKeyFil = w1;
try {
cFac = CertificateFactory.getInstance(certType);
cert = cFac.generateCertificate(fis);
} catch (CertificateException e1) {
log.println(formMessage("pukynotgtb", certFileName));
return errMeld(19, e1);
}
pubKey = cert.getPublicKey();
log.println(formMessage("pukyfrcert", certFileName));
} else { // cert else keystore
keyAlias = w0;
pubKeyFil = w1;
storePassW = TextHelper.trimUq(storePassW, null);
if (storePassW != null) {
passWord = storePassW.toCharArray();
} else {
passWord = AskDialog.getAnswerText(
valueLang("pkexpdtit"), // titel PKextr password dialog
keyAlias, // upper
true, // upper monospaced
valueLang("pkexpasye"), valueLang("pkexpasno"), // yes, cancel
990, // waitMax = 99s
valueLang("pkexpassr"),
null, true); //Color bg password
} // storePassw
try {
ks = KeyStore.getInstance(certType);
} catch (KeyStoreException e1) {
return errMeld(21, e1);
}
try {
ks.load(fis, passWord);
fis.close();
cert = ks.getCertificate(keyAlias);
} catch (Exception e2) {
return errMeld(29, e2);
}
// alias
if (exportPrivate) {
Key key = null;
try {
key = ks.getKey(keyAlias, passWord);
} catch (Exception e) {
return errorExit(39, e, "could not get the key from keystore.");
}
if (key instanceof PrivateKey) {
// ex history: BASE64Encoder myB64 = new BASE64Encoder();
// since 24.06.2016 String b64 = myB64.encode(key.getEncoded());
Base64.Encoder mimeEncoder = java.util.Base64.getMimeEncoder();
String b64 = mimeEncoder.encodeToString( key.getEncoded());
log.println("-----BEGIN PRIVATE KEY-----");
log.println(b64);
log.println("-----END PRIVATE KEY-----\n\n");
if (pubKeyFil == null) return 0;
log.println(formMessage("prkywrite", pubKeyFil));
try {
keyfos = new FileOutputStream(pubKeyFil);
} catch (FileNotFoundException e1) {
return errMeld(31, e1);
}
Writer osw = new OutputStreamWriter(keyfos);
try {
osw.write("-----BEGIN PRIVATE KEY-----\n");
osw.write(b64);
osw.write("\n-----END PRIVATE KEY-----\n");
osw.close();
} catch (IOException e2) {
return errMeld(31, e2);
}
return 0;
}
return errorExit(39, "no private key found");
} // export private
pubKey = cert.getPublicKey();
log.println(formMessage("pukyfrstor",
new String[]{keyAlias, certFileName}));
} // else from keystore
log.println("\n----\n" + pubKey.toString() + "\n----\n\n");
if (pubKeyFil == null) return 0;
// got the public key and listed it to log
log.println(formMessage("pukywrite", pubKeyFil));
//" Write public key to \"" + pubKeyFil + "\"\n");
byte[] key = pubKey.getEncoded();
try {
keyfos = new FileOutputStream(pubKeyFil);
} catch (FileNotFoundException e1) {
return errMeld(31, e1);
}
try {
keyfos.write(key);
keyfos.close();
} catch (IOException e2) {
return errMeld(31, e2);
}
return 0;
} // doIt()
} // PKextr (27.12.2004, 09.02.2005, 07.11.2008, 24.06.2016)