I'm writing a program in Java that generates RSA keys using Crt parameters. I'm able to export the key in a .pem file, but I need to export it in a pkcs12 file. How can I do that in Java?
Java contains native support for PKCS#12 key stores through KeyStore.getInstance("PKCS12"). However, generally the key stores require you to offer a matching private key / certificate pair. Just providing a public key instead of a certificate is not enough. You must create a certificate, for instance a self signed certificate to be able to use the PKCS#12 key store provider.
I've tried to create my own Certificate instance using an anonymous type, but the PKCS#12 key store only seems to allow X.509 certificates (but it will only tell you when you store the key store, i.e. it is not fail fast.
Here's some code to create the self signed certificate and store the private key and resulting self signed certificate:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.encoders.Hex;
public class StoreRSAKeyPairInPKCS12 {
public static void main(String[] args) throws Exception {
// --- generate a key pair (you did this already it seems)
KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA");
final KeyPair pair = rsaGen.generateKeyPair();
// --- create the self signed cert
Certificate cert = createSelfSigned(pair);
// --- create a new pkcs12 key store in memory
KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
pkcs12.load(null, null);
// --- create entry in PKCS12
pkcs12.setKeyEntry("privatekeyalias", pair.getPrivate(), "entrypassphrase".toCharArray(), new Certificate[] {cert});
// --- store PKCS#12 as file
try (FileOutputStream p12 = new FileOutputStream("mystore.p12")) {
pkcs12.store(p12, "p12passphrase".toCharArray());
}
// --- read PKCS#12 as file
KeyStore testp12 = KeyStore.getInstance("PKCS12");
try (FileInputStream p12 = new FileInputStream("mystore.p12")) {
testp12.load(p12, "p12passphrase".toCharArray());
}
// --- retrieve private key
System.out.println(Hex.toHexString(testp12.getKey("privatekeyalias", "entrypassphrase".toCharArray()).getEncoded()));
}
private static X509Certificate createSelfSigned(KeyPair pair) throws OperatorCreationException, CertIOException, CertificateException {
X500Name dnName = new X500Name("CN=publickeystorageonly");
BigInteger certSerialNumber = BigInteger.ONE;
Date startDate = new Date(); // now
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
calendar.add(Calendar.YEAR, 1);
Date endDate = calendar.getTime();
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").build(pair.getPrivate());
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate, dnName, pair.getPublic());
return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner));
}
}
You will need at least the PKIX library (bcpkix-jdk15on.jar) from Bouncy Castle and possibly the library of the Bouncy Castle provider. Installing the Bouncy Castle provider is not required.
Related
I have .cer certificate. When I'm opening it, it shows me certificate chain.
Using this code, I read the certificate to x509certificate file.
File certificateFile = new File("C:\\Users\\grish\\Desktop\\certificateForValidation.cer");
InputStream inputStream = new FileInputStream(certificateFile);
X509Certificate certificate = new X509CertImpl(inputStream);
I want to get certificate chain from that file ! (end-entity, CA, Root)
How can I do that programmatically in java.
With C#, this is much more easy
X509Chain ch = new X509Chain();
ch.ChainPolicy.RevocationMode = X509RevocationMode.Online;
ch.Build (certificate);
And then I can get all certificates from ch.
First of all make sure you are using the java.security.cert package; see JDK 17 API doc for java.security.cert.
Avoid using javax.security.cert. The package is marked as deprecated an will be removed in future releases; see JDK 17 API doc for javax.security.cert.
The following code will return a collection of java.security.cert.X509Certificate objects. In the case of your example with the certificate of medium.com the list will contain three items; one for every certificate in the chain.
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.List;
public static void main(String[] args) {
try (InputStream inStream = new FileInputStream("C:\\Users\\grish\\Desktop\\Coding Space\\Certificates\\certificateForValidation.p7b")) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<X509Certificate> certs = (List<X509Certificate>) cf.generateCertificates(inStream);
} catch (Exception ex) {
ex.printStackTrace();
}
}
I tested it with a PEM formated chain but it should work with PCKS#7 (.p7b file extension) as well; see the documentation of the generateCertificates(InputStream inStream) method.
Find more information about x.509 encoding and conversion here
I'm trying to generate my own CSR for my keystore, but it didn't go well and that error is confusing me. Here is my code:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
public class CreateKeyTest {
public static void main(String[] args) throws OperatorCreationException, IOException, GeneralSecurityException {
KeyPairGenerator kpg;
KeyPair kp;
RSAPublicKey pubKey;
RSAPrivateKey privKey;
FileOutputStream out;
KeyStore ks;
FileInputStream in;
FileInputStream bFis;
try {
ks = KeyStore.getInstance("JKS");
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
kp = kpg.generateKeyPair();
pubKey = (RSAPublicKey) kp.getPublic();
privKey = (RSAPrivateKey) kp.getPrivate();
// generate CSR
ContentSigner sign = new JcaContentSignerBuilder("SHA1withRSA").build(privKey);
X500NameBuilder nBuilder = new X500NameBuilder();
nBuilder.addRDN(BCStyle.CN, "TestCSR");
nBuilder.addRDN(BCStyle.C, "ER");
nBuilder.addRDN(BCStyle.E, "test#test.com");
X500Name name = nBuilder.build();
PKCS10CertificationRequestBuilder cerReq = new JcaPKCS10CertificationRequestBuilder(name, pubKey);
PKCS10CertificationRequest request = cerReq.build(sign);
PEMWriter pWr = new PEMWriter(new FileWriter(new File("D:\\test.csr")));
pWr.writeObject(request);
pWr.flush();
pWr.close();
bFis = new FileInputStream("D:\\test.csr");
BufferedInputStream ksbufin = new BufferedInputStream(bFis);
X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(ksbufin);
ks.setKeyEntry("RSA_key", kp.getPrivate(), "changeit".toCharArray(),
new java.security.cert.Certificate[] { certificate });
out = new FileOutputStream("key.store");
ks.store(out, "changeit".toCharArray());
System.out.println("New Keystore Generated");
out.close();
} catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException
| OperatorCreationException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
When I execute it, it showed me the exception:X509.ObjectIdentifier() -- data isn't an object ID (tag = 49), and it could be back-traced to generateCertificate(ksbufin). But I checked test.cer and it do have certificate data in there, and that exception message confused me, don't even know what does that mean(object ID? tag = 49? I didn't see I generated an ID in my code.).
Can anyone help me out this mud?
The error message is correct, test.csr does not contain a certificate. You have built it using a PKCS10CertificationRequest, so it consenquently contains a Certificate Signing Request (CSR).
You have generated a key pair, private and public, and a CSR. The CSR is a request of a certificate to a Certification Authority (CA). It contains the public key and some expected attributes for the certificate (CN, C, OU, etc). CSR is signed with the private key and has to be sent to CA. The CA will extract the public key, generates a certificate and signs it. See Certificate enrollment process
If you want a Certificate, you need to get signed the certificate by the CA
I had to generate x509 self-signed certificate
How can one distract sertificate and private key file from this code? Is it possible? Are there other ways how to generate x509 self-signed certificate?
Please help me.
import java.io.PrintWriter;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory;
import org.bouncycastle.x509.X509V3CertificateGenerator;
public class javaskods {
public static X509Certificate generateV3Certificate(KeyPair pair) throws InvalidKeyException,
NoSuchProviderException, SignatureException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setIssuerDN(new X500Principal("CN=Test Certificate"));
certGen.setNotBefore(new Date(System.currentTimeMillis() - 10000));
certGen.setNotAfter(new Date(System.currentTimeMillis() + 10000));
certGen.setSubjectDN(new X500Principal("CN=Test Certificate"));
certGen.setPublicKey(pair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature
| KeyUsage.keyEncipherment));
certGen.addExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(
KeyPurposeId.id_kp_serverAuth));
certGen.addExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames(
new GeneralName(GeneralName.rfc822Name, "test#test.test")));
return certGen.generateX509Certificate(pair.getPrivate(), "BC");
}
public static void main(String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyPair pair = generateRSAKeyPair();
X509Certificate cert = generateV3Certificate(pair);
cert.checkValidity(new Date());
cert.verify(cert.getPublicKey());
}
public static KeyPair generateRSAKeyPair() throws Exception {
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
kpGen.initialize(1024, new SecureRandom());
return kpGen.generateKeyPair();
}
}
You can generate a self-signed certificates using keytool (part of JDK) or with other utilities.
Java Keytool: http://docs.oracle.com/javase/7/docs/technotes/tools/windows/keytool.html
Keytool-ui: https://code.google.com/p/keytool-iui/
keytool -genkey -keystore ./myjks -keyalg RSA -keysize 2048 -validity 999 -alias mykey
This will create a new private key for you in the myjks file.
Then in your code you can refer to the certificate via alias name and jks file.
If you are encrypting, you will want to use keytool to export the public key and share it with the recipient of the encrypted data.
keytool -export -keystore ./myjks -file mykey_pub.cer -alias mykey
The above command will export a certificate which contains the public key.
I am trying to sign a pdf document in java using a USB e-token.I want to read the signature from USB token safenet (alladin etoken pro 72 k(Java)) and attach to pdf using java code.I have done digital signature signing using a key stored in my local machine.But i want to know how the same can be done using a USB e-token.
The whole point of a USB token for signing is, that nobody can read the secret key from that device. So you sent the hash to the token and the token will send you the signature back.
For this to work you need a JCE provider which can talk to the token. This is typically done either by PKCS#11 (the token delivers a library for this) or the token delivers a MSCAPI driver (under windows).
Both can be used under Java, the PKCS#11 way might be a bit more complicated to setup, but in my experience it is better for automated signing because in the MSCAPI case you often need to enter the token PIN manually.
If your token is recognized by windows the following command should see and list its key:
keytool -list -storetype Windows-MY
The Windows Keystore can then be used to get a handle of the key for signing, but you can also use it to export a copy of the public key.
You can use SUN PKCS11 provider to refer the keys in the Etoken.You can just try the below code
String pkcs11Config = "name=eToken\nlibrary=C:\\Windows\\System32\\eps2003csp11.dll";
java.io.ByteArrayInputStream pkcs11ConfigStream = new java.io.ByteArrayInputStream(pkcs11Config.getBytes());
sun.security.pkcs11.SunPKCS11 providerPKCS11 = new sun.security.pkcs11.SunPKCS11("pkcs11Config");
java.security.Security.addProvider(providerPKCS11);
// Get provider KeyStore and login with PIN
String pin = "12345678";
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS11", providerPKCS11);
KeyStore keyStore=KeyStore.getInstance("PKCS11",providerPKCS11);
keyStore.load(null, pin.toCharArray());
// Enumerate items (certificates and private keys) in the KeyStore
java.util.Enumeration<String> aliases = keyStore.aliases();
String alias = null;
while (aliases.hasMoreElements()) {
alias = aliases.nextElement();
System.out.println(alias);
}
Try this code
import com.lowagie.text.pdf.*;
import com.lowagie.text.Rectangle;
//import com.lowagie.text.pdf.pdfSignatureAppearance;
//import com.lowagie.text.pdf.pdfStamper;
import java.security.*;
import java.io.*;
import java.awt.*;
import java.security.cert.*;
import java.lang.*;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.CertPath;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.List;
public class pdfsign1{
public static void main(String args[]) {
try {
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);[/b]
PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
//sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
sap.setReason("I'm the author");
sap.setLocation("Lisbon");
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
stp.close();
}
catch(Exception e) {}
}
}
I have a RSA Key and a X.509 Certificate which I use for SSL connections.
The key and certificate are stored in files in PEM format (generated by OpenSSL) and used in an Apache HTTP server environment.
Is there an easy way to validate if the key matches the certificate using only Java code (without executing the openssl binary and parsing the output), for example by using Java security and/or Bouncycastle library methods?
The following code compares the SHA-1 over the modulus within the public and private key. The modulus should be unique for each pair (unless you key pair generation mechanism or random generator is broken of course).
Note that the following code requires the key to be in unencrypted PKCS#8 format. It may be better to use PKCS#12 instead and load the binary PKCS#12 file in a KeyStore (providing the password).
openssl pkcs8 -topk8 -in key.pem -out keypk8.pem -nocrypt
And finally the Java code:
import static org.bouncycastle.util.encoders.Hex.toHexString;
import java.io.ByteArrayInputStream;
import java.io.FileReader;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
public class CompareCertAndKey {
/**
* Checks if the certificate and RSA private key match.
*
* #param args the path to the certificate file in args[0] and that of the private key in args[1]
*/
public static void main(String[] args) {
try {
final PemReader certReader = new PemReader(new FileReader(args[0]));
final PemObject certAsPemObject = certReader.readPemObject();
if (!certAsPemObject.getType().equalsIgnoreCase("CERTIFICATE")) {
throw new IllegalArgumentException("Certificate file does not contain a certificate but a " + certAsPemObject.getType());
}
final byte[] x509Data = certAsPemObject.getContent();
final CertificateFactory fact = CertificateFactory.getInstance("X509");
final Certificate cert = fact.generateCertificate(new ByteArrayInputStream(x509Data));
if (!(cert instanceof X509Certificate)) {
throw new IllegalArgumentException("Certificate file does not contain an X509 certificate");
}
final PublicKey publicKey = cert.getPublicKey();
if (!(publicKey instanceof RSAPublicKey)) {
throw new IllegalArgumentException("Certificate file does not contain an RSA public key but a " + publicKey.getClass().getName());
}
final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
final byte[] certModulusData = rsaPublicKey.getModulus().toByteArray();
final MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
final byte[] certID = sha1.digest(certModulusData);
final String certIDinHex = toHexString(certID);
final PemReader keyReader = new PemReader(new FileReader(args[1]));
final PemObject keyAsPemObject = keyReader.readPemObject();
if (!keyAsPemObject.getType().equalsIgnoreCase("PRIVATE KEY")) {
throw new IllegalArgumentException("Key file does not contain a private key but a " + keyAsPemObject.getType());
}
final byte[] privateKeyData = keyAsPemObject.getContent();
final KeyFactory keyFact = KeyFactory.getInstance("RSA");
final KeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyData);
final PrivateKey privateKey = keyFact.generatePrivate(keySpec);
if (!(privateKey instanceof RSAPrivateKey)) {
throw new IllegalArgumentException("Key file does not contain an X509 encoded private key");
}
final RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
final byte[] keyModulusData = rsaPrivateKey.getModulus().toByteArray();
final byte[] keyID = sha1.digest(keyModulusData);
final String keyIDinHex = toHexString(keyID);
System.out.println(args[0] + " : " + certIDinHex);
System.out.println(args[1] + " : " + keyIDinHex);
if (certIDinHex.equalsIgnoreCase(keyIDinHex)) {
System.out.println("Match");
System.exit(0);
} else {
System.out.println("No match");
System.exit(-1);
}
} catch (Exception e) {
e.printStackTrace(System.err);
System.exit(-2);
}
}
}
Thank you very much for the above code snippet. Its working for me with bouncycastle version 1.51
<bcprov-jdk15on-version>1.51</bcprov-jdk15on-version>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bcprov-jdk15on-version}</version>
</dependency>
Many Thanks for your code snippet.