Getting a PrivateKey object from a .p12 file in Java - java

As the title suggests, I have .p12 file required for google service account api access. In order to get the credential to connect to the api, there's a field .setServiceAccountPrivateKey(PrivateKey privateKey). So, what's the easiest way in which I can do this? I have a resources folder which is in my classpath so if I add the p12 file there, I can get the resource from getClass().getResource() as either an inputStream or a URL. I've tried the URL method but it doesn't work (I get a "URI is not hierarchical" error trying to create a File object from URL.toURI()).

You can load your .p12 file using the ClassLoader.getResourceAsStream(String) method, load it to a KeyStore and them get the key from the KeyStore.
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(this.getClass().getClassLoader().getResourceAsStream("keyFile.p12"), p12Password.toCharArray());
PrivateKey key = (PrivateKey)keystore.getKey(keyAlias, p12Password.toCharArray());
ClassLoader.getResourceAsStream(String) loads resources from any location provided they're already on the classpath, there's no need to specify a path to the file.
keyAlias is the name of the entry in your p12 file that corresponds to the private key. PKCS12 files can contain multiple entries, so you need some way to indicate which entry you want to access. The alias is how this is achieved.
If you're not sure what the alias for your private key is, you can use the keytool utility from the command line to list the contents of your p12 file. This tool is included with all JRE and JDK installations.
keytool -list -keystore keyFile.p12 -storepass password -storetype PKCS12
Output
Keystore type: PKCS12
Keystore provider: SunJSSE
Your keystore contains 1 entry
yourKeyAlias, Sep 4, 2013, PrivateKeyEntry,
Certificate fingerprint (MD5): 48:A8:C4:12:8E:4A:8A:AD:58:81:26:90:E7:3D:C8:04

I think it's easier to call Google's SecurityUtils directly, e.g.:
PrivateKey privateKey = SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(), this.getClass().getResourceAsStream("keyFile.p12"), "notasecret", "privatekey", "notasecret")
It's one-line and you don't have to worry about aliasing.

If you get null from getKey() (eg. you are using BouncyCastle as a provider) you should find the last keyAlias element:
KeyStore keystore = KeyStore.getInstance("PKCS12", "BC");
keystore.load(this.getClass().getClassLoader().getResourceAsStream("keyFile.p12"), p12Password.toCharArray());
Enumeration aliases = keystore.aliases();
String keyAlias = "";
while (aliases.hasMoreElements()) {
keyAlias = (String) aliases.nextElement();
}
PrivateKey key = (PrivateKey)keystore.getKey(keyAlias, pass);

The above suggestions did not work for me. Then I tried the one at http://www.java2s.com/Code/Java/Security/RetrievingaKeyPairfromaKeyStore.htm and it worked. Copy pasting it below
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
public class Main {
public static void main(String[] argv) throws Exception {
FileInputStream is = new FileInputStream("your.keystore");
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, "my-keystore-password".toCharArray());
String alias = "myalias";
Key key = keystore.getKey(alias, "password".toCharArray());
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
new KeyPair(publicKey, (PrivateKey) key);
}
}
}

Related

How to display Java keystore SecretKeyEntry from command line

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.

How to access certificates and private key from etoken using java

I am working with digital certificate and digital signature. We got pfx file from the vendor. We convert this pfx file to java key store and used it to create the digital signature using java program. Now the vendor has etoken hardware. They give me cer file in place pf pfx. I converted cer to jks java key store and used it in my program... My program told me that private key is not there. I have found that there is no private key with cer file. I have talked to vendor about this he told me private key can not be extracted from the etoken.. you must directly access the etoken through program to get the private key. Can anybody tell me how do i access etoken programetically. Is there any java api which is used to access etoken directly. Help me....
Private key can be extracted using PKCS11.
To extract Private key from eToken in java, you need to pass config file to sun.security.pkcs11.SunPKCS11 instance.
Config file must have following properties:
name=<Name of Etoken>
slot=<slot number for etoken>
library=<path of the pckcs11 library(dll) for that etoken>
Following is sample code to extract private key using eToken
PrivateKey privateKey = null;
char password[] = "1234".toCharArray();
Provider userProvider = new sun.security.pkcs11.SunPKCS11("D:\\config.cfg");
ks = KeyStore.getInstance("PKCS11", userProvider);
ks.load(null, password);
Enumeration e = ks.aliases();
String alias=null;
while (e.hasMoreElements())
{
alias = (String) e.nextElement();
privateKey = (PrivateKey) ks.getKey(alias, password);
}

How to export multiple public certificate in one crt file

I have a keystore.jks file with multiple certificate including public and private key.
now i want to know how to export all public key certificate into new file all-public-cert.crt file.
This "all-public-cert.crt" file contain only certificate (public key only) . should not contain any private key in this file.
after this i would like to ready this "all-public-cert.crt" file via java code and validate public and private key using challenge response.
kindly guide me or suggest me some reference document or url.
Note : i am able to use any tool like openssl or keytool.
Thanks & Regards,
Gaurav Paliwal
Normally a keystore i.e., your keystore.jks contains private keys and the corresponding X.509 certificates. But the X.509 certificates do NOT include a private key, as you falsely assumed:
I have a keystore.jks file with multiple certificate including public and private key
This "all-public-cert.crt" file contain only certificate (public key only) . should not contain any private key in this file.
This means that your X.509 certificates do only contain public key information, no private key information.
In order to fetch a certificate form your keystore (leaving the error handling aside):
String keystorePath = ....
InputStream is = new FileInputStream(new File(keystorePath));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, "keystorePassword".toCharArray());
Enumeration e = ks.aliases();
Set<Certificate> certs = new HashSet<Certificate>();
while (e.hasMoreElements()) {
Certificate cert = ks.getCertificate((String) enumeration.nextElement());
certs.add(cert);
}
Now you have all certificates in your certs Set. But why do you need to put them all into the all-public-cert.crt.
1.) First you cannot put multiple certificates in one file, and hope that the one file can be used the "normal" way (e.g. double click to open it, import it into other applications, ..). The file will be garbage and can only be read from e.g., your application
2.) Therefore the file extension should not be .crt, it should be .myExportedCertificates or something like that.
I think you just want to store the certificates on the file system in order to use them afterwards. In this case simply use this code (error handling is your job):
String pathToStoreTheCerts = ...
File path = new File(pathToStoreTheCerts);
OutputStream os = null;
for (X509Certificate cert : certs) {
File certFile = new File(path, cert.getSubjectX500Principal().getName() + ".crt");
os = new FileOutputStream(certFile);
os.write(cert.getEncoded());
os.flush();
}
os.close();

iText: password trouble implementing digital signature for PDF file on server

New user for iText trying to get example code adapted from sample 3.1 and code sample 3.2 in https://itextpdf.com/book/digitalsignatures
I've included code snippets below FYI.
I had the hosting provider create a .pfx file from a GlobalSign certificate (they don't provide .p12 file). They issued the following linux command:
# openssl pkcs12 -export -out cert.pfx -inkey www.mydomain.com.key
-in ../certs/www.mydomain.com.crt -certfile ../certs/ca-bundle.crt
where ca-bundle.crt comes from GlobalSign. To get a .p12, I just copied the .pfx file into a new file with a .p12 extension. From what I can see online, people have done this before (not related to iText) and succeeded, as the .pfx file and .p12 files are binary equivalents.
To verify, one can type at the linux prompt:
openssl pkcs12 -info -in /path/to/file/cert.pfx
and it then asks for an import password, and there is none, so just hit enter, and then it asks for the private pass phrase, which I enter (e.g. myPrivateCertPassword), and then it displays my private cert, GlobalSign's public cert, and my private key.
Now's where I get confused. There is no import password, so I've tried the following attempts in iText (as shown in the snippets below):
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), null);
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), "".toCharArray());
neither one works. The first gives a runtime error:
java.io.IOException: PKCS12 key store mac invalid - wrong password or corrupted file.
and the second one gives run time error:
java.lang.NoClassDefFoundError : org/bouncycastle/cert/X509CertificateHolder
Any ideas what could be wrong?
Where, exactly, does the password used in the ks.load() line of code come from (GlobalSign? the server hosting company? me?). Is this called the export key when I issue the above export command?
Thanks in advance for any comments.
-------- code snippets follow ---------
public static final char[] PASS = "myPrivateCertPassword".toCharArray();
public static final String CERTIFICATE_PATH = "/path/to/file/cert.p12";
In main():
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("pkcs12", provider.getName());
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), null);
// ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), "".toCharArray()); // this also fails
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, CONSTANTS.PASS);
Certificate[] chain = ks.getCertificateChain(alias);
sign(userFile, userFile_signed, chain, pk, DigestAlgorithms.SHA256,
provider.getName(),CryptoStandard.CMS, "Test", "Ghent", null, null, null, 0);
Outside main():
public void sign(
String src,
String dest,
Certificate[] chain,
PrivateKey pk,
String digestAlgorithm,
String provider,
CryptoStandard subfilter,
String reason,
String location,
Collection<CrlClient> crlList,
OcspClient ocspClient, TSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain,
crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
UPDATE 1:
I created a new cert.pfx file by exporting as above, but this time I entered an "export password", as exportPassword, which I inserted as:
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), "exportPassword".toCharArray());
but this gives a new runtime error:
java.io.IOException: exception decrypting data - java.security.InvalidKeyException: Illegal key size
Am i getting closer?
UPDATE 2:
I installed JCE per Bruno's comment, and now I get this error:
java.lang.NoClassDefFoundError : org/bouncycastle/tsp/TimeStampTokenInfo
UPDATE 3:
I was able to clear the above error by also adding bcpkix-jdk15on-149.jar from www.bouncycastle.org to the /lib directory, and adding the following to the java program:
import org.bouncycastle.jce.provider.BouncyCastleProvider; // this was already there
import org.bouncycastle.tsp.TimeStampTokenInfo; // this is new and fixed the above error
Now I can see the digital signature!
UPDATE 4:
For those interested, see my follow-up post here:
iText: what type of certificates do people use to automate PDF signing on Linux?

Convert Keystore - Windows-my to jks

We are using SunMSCAPI to retrieve the current user keystore as below..
keystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
We will need to build a JSSE keystore of type JKS/PKCS12 and pass it to an app..
Sort of beginning to understand how this works.. Any help would be appreciated.
Did you try something like
keystore.load(inputStreamFromOriginalFile, password);
KeyStore keystore2 = KeyStore.getInstance("JKS");
for (String name : toIterable(keystore.aliases())) {
Entry entry = keystore.getEntry(name, protParam);
keystore2.setEntry(name, entry, protParam);
}
keystore2,store(outputStream, password);
I mean a dump copy of all entries into a new keystore2.

Categories

Resources