package org.bouncycastle.test.est.examples;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.est.ESTAuth;
import org.bouncycastle.est.ESTService;
import org.bouncycastle.est.EnrollmentResponse;
import org.bouncycastle.est.jcajce.JcaHttpAuthBuilder;
import org.bouncycastle.est.jcajce.JcaJceUtils;
import org.bouncycastle.est.jcajce.JsseDefaultHostnameAuthorizer;
import org.bouncycastle.est.jcajce.JsseESTServiceBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.test.est.BCChannelBindingProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;
/**
* Enroll example exercises the enrollment of a certificate.
* It will generate a CSR using the supplied common name.
* As this is a PEM encoded trusted operation a trust anchor will need to be provided.
*/
public class EnrollExample
{
public EnrollExample(String[] args)
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
File trustAnchorFile = null;
String serverRootUrl = null;
String cn = null;
File clientKeyStoreFile = null;
char[] clientKeyStoreFilePassword = null;
String keyStoreType = null;
boolean httpAuth = false;
String[] credentials = null;
boolean reEnroll = false;
String tlsVersion = "TLS";
String tlsProvider = "BCJSSE";
String tlsProviderClass = "org.bouncycastle.jsse.provider.BouncyCastleJsseProvider";
boolean noNameVerifier = false;
boolean pop = false;
int timeout = 0;
String label = null;
String saveKeysToFile = null;
String keyFile = null;
String suffixList = null;
try
{
for (int t = 0; t < args.length; t++)
{
String arg = args[t];
if (arg.equals("-r"))
{
reEnroll = true;
}
else if (arg.equals("-t"))
{
trustAnchorFile = ExampleUtils.nextArgAsFile("Trust Anchor File", args, t);
t += 1;
}
else if (arg.equals("-u"))
{
serverRootUrl = ExampleUtils.nextArgAsString("Server Hostname", args, t);
t += 1;
}
else if (arg.equals("-c"))
{
cn = ExampleUtils.nextArgAsString("Common Name", args, t);
t += 1;
}
else if (arg.equals("--keyStore"))
{
clientKeyStoreFile = ExampleUtils.nextArgAsFile("Client Key store", args, t);
t += 1;
}
else if (arg.equals("--keyStorePass"))
{
clientKeyStoreFilePassword = ExampleUtils.nextArgAsString("Keystore password", args, t).toCharArray();
t += 1;
}
else if (arg.equals("--keyStoreType"))
{
keyStoreType = ExampleUtils.nextArgAsString("Keystore type", args, t);
t += 1;
}
else if (arg.equals("--keyStoreType"))
{
keyStoreType = ExampleUtils.nextArgAsString("Keystore type", args, t);
t += 1;
}
else if (arg.equals("--auth"))
{
credentials = ExampleUtils.nextArgAsString("Keystore type", args, t).split(":");
httpAuth = true;
t += 1;
}
else if (arg.equals("--tls"))
{
tlsVersion = ExampleUtils.nextArgAsString("TLS version", args, t);
t += 1;
}
else if (arg.equals("--tlsProvider"))
{
tlsProvider = ExampleUtils.nextArgAsString("TLS Provider", args, t);
t += 1;
tlsProviderClass = ExampleUtils.nextArgAsString("TLS Provider Class", args, t);
t += 1;
}
else if (arg.equals("--pop"))
{
pop = true;
}
else if (arg.equals("--to"))
{
timeout = ExampleUtils.nextArgAsInteger("Timeout", args, t);
t += 1;
}
else if (arg.equals("--no-name-verifier"))
{
noNameVerifier = true;
}
else if (arg.equals("--label"))
{
label = ExampleUtils.nextArgAsString("CA Label", args, t);
t += 1;
}
else if (arg.equals("--save"))
{
saveKeysToFile = ExampleUtils.nextArgAsString("Save keys to file", args, t);
t += 1;
}
else if (arg.equals("--load"))
{
keyFile = ExampleUtils.nextArgAsString("Load keys from file", args, t);
t += 1;
} else if (arg.equals("--sl"))
{
suffixList = ExampleUtils.nextArgAsString("Suffix List", args, t);
t += 1;
}
else
{
System.out.println("Unknown argument: " + arg);
printArgs();
System.exit(0);
}
}
}
catch (IllegalArgumentException ilex)
{
System.err.println(ilex.getMessage());
System.exit(1);
}
if (args.length == 0)
{
printArgs();
System.exit(0);
}
if (serverRootUrl == null)
{
System.err.println("Server url (-u) must be defined.");
System.exit(-1);
}
if (cn == null)
{
System.err.println("Common Name (-c) must be defined.");
System.exit(-1);
}
if (trustAnchorFile == null)
{
System.err.println("Trust Anchor (-t) must be defined.");
System.exit(-1);
}
if (tlsProviderClass != null)
{
Security.addProvider((Provider)Class.forName(tlsProviderClass).newInstance());
}
if (suffixList == null)
{
System.err.println("Known Suffix List (--sl) must be defined.");
System.exit(-1);
}
//
// Make a CSR here
//
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("prime256v1");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDSA", "BC");
kpg.initialize(ecGenSpec, new SecureRandom());
KeyPair keyPair = null;
if (keyFile != null)
{
PemReader pr = new PemReader(new FileReader(keyFile));
PemObject o = null;
JcaPEMKeyConverter foo = new JcaPEMKeyConverter().setProvider(new BouncyCastleProvider());
PrivateKey privateKey = null;
PublicKey publicKey = null;
while ((o = pr.readPemObject()) != null)
{
if ("PRIVATE KEY".equals(o.getType()))
{
privateKey = foo.getPrivateKey(PrivateKeyInfo.getInstance(o.getContent()));
}
else if ("PUBLIC KEY".equals(o.getType()))
{
publicKey = foo.getPublicKey(SubjectPublicKeyInfo.getInstance(o.getContent()));
}
else
{
System.err.println("Unrecognised type: " + o.getType());
System.exit(1);
}
}
if (publicKey == null)
{
throw new IllegalArgumentException("No public key was found.");
}
if (privateKey == null)
{
throw new IllegalArgumentException("No private key was found.");
}
keyPair = new KeyPair(publicKey, privateKey);
}
else
{
keyPair = kpg.generateKeyPair();
}
PKCS10CertificationRequestBuilder pkcs10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Name("CN=" + cn),
keyPair.getPublic());
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WITHECDSA").setProvider("BC").build(keyPair.getPrivate());
JsseESTServiceBuilder est = new JsseESTServiceBuilder(serverRootUrl, JcaJceUtils.getCertPathTrustManager(ExampleUtils.toTrustAnchor(ExampleUtils.readPemCertificates(trustAnchorFile)), null));
est.withTimeout(timeout);
est.withLabel(label);
est.withTLSVersion(tlsVersion);
est.withProvider(tlsProvider);
// sfcb.withSecureRandom(new SecureRandom());
if (clientKeyStoreFile != null)
{
if (keyStoreType == null)
{
keyStoreType = "JKS";
}
KeyStore ks = KeyStore.getInstance(keyStoreType, "BC");
ks.load(new FileInputStream(clientKeyStoreFile), clientKeyStoreFilePassword);
est.withKeyManagers(JcaJceUtils.createKeyManagerFactory("X509", null, ks, clientKeyStoreFilePassword).getKeyManagers());
}
if (noNameVerifier)
{
est.withHostNameAuthorizer(null);
}
else
{
est.withHostNameAuthorizer(new JsseDefaultHostnameAuthorizer(SuffixList.loadSuffixes(suffixList)));
}
ESTAuth auth = null;
if (httpAuth)
{
if (credentials.length == 3)
{
auth = new JcaHttpAuthBuilder(credentials[0], credentials[1], credentials[2].toCharArray())
.setNonceGenerator(new SecureRandom()).setProvider("BC").build();
}
else if (credentials.length == 2)
{
auth = new JcaHttpAuthBuilder(null, credentials[0], credentials[1].toCharArray())
.setNonceGenerator(new SecureRandom()).setProvider("BC").build();
}
else
{
System.err.println("Not enough credential for digest auth.");
System.exit(0);
}
}
est.withTimeout(timeout);
EnrollmentResponse enrollmentResponse;
//
// The enrollment action can be deferred by the server.
// In this example we will check if the response is actually completed.
// If it is not then we must wait long enough for it to be completed.
//
do
{
if (pop)
{
est.withChannelBindingProvider(new BCChannelBindingProvider());
ESTService estService = est.build();
enrollmentResponse = estService.simpleEnrollPoP(reEnroll, pkcs10Builder, contentSigner, auth);
}
else
{
ESTService estService = est.build();
PKCS10CertificationRequest csr = pkcs10Builder.build(contentSigner);
enrollmentResponse = estService.simpleEnroll(reEnroll, csr, auth);
}
if (!enrollmentResponse.isCompleted())
{
long t = enrollmentResponse.getNotBefore() - System.currentTimeMillis();
if (t < 0)
{
continue;
}
t += 1000;
Thread.sleep(t);
continue;
}
}
while (!enrollmentResponse.isCompleted());
for (X509CertificateHolder holder : ESTService.storeToArray(enrollmentResponse.getStore()))
{
//
// Limited the amount of information for the sake of the example.
// The default too string prints everything and is hard to follow.
//
System.out.println("Subject: " + holder.getSubject());
System.out.println("Issuer: " + holder.getIssuer());
System.out.println("Serial Number: " + holder.getSerialNumber());
System.out.println("Not Before: " + holder.getNotBefore());
System.out.println("Not After: " + holder.getNotAfter());
System.out.println();
System.out.println(ExampleUtils.toJavaX509Certificate(holder));
}
if (saveKeysToFile != null)
{
PemWriter pw = new PemWriter(new FileWriter(saveKeysToFile));
pw.writeObject(new JcaPKCS8Generator(keyPair.getPrivate(), null));
pw.writeObject(new JcaMiscPEMGenerator(keyPair.getPublic(), null));
pw.flush();
pw.close();
}
}
public static void main(String[] args)
throws Exception
{
try
{
new EnrollExample(args);
}
catch (Exception ex)
{
System.out.println("\n\n-----------------");
System.out.println(ex.getMessage());
System.out.println("-----------------\n\n");
throw ex;
}
}
public void printArgs()
{
System.out.println("-r Re-enroll");
System.out.println("-t <file> Trust anchor file");
System.out.println("-u <url> EST hostname url.");
System.out.println("-c <common name> EST CN.");
System.out.println("--keyStore <file> Optional Key Store.");
System.out.println("--keyStorePass <password> Optional Key Store password.");
System.out.println("--keyStoreType <JKS> Optional Key Store type, defaults to JKS");
System.out.println("--auth <realm:user:password> Auth credentials, if real is not");
System.out.println("--tls <version> Use this TLS version when creating socket factory, Eg TLSv1.2");
System.out.println("--tlsProvider <provider> <class> The JSSE Provider.");
System.out.println("--pop Turn on PoP");
System.out.println("--to <milliseconds> Timeout in milliseconds.");
System.out.println("--no-name-verifier No hostname verifier.");
System.out.println("--label <ca label> CA Label.");
System.out.println("--save <path to file> Save generated public and private key to file, (PEM)");
System.out.println("--load <path to file> Load generated public and private key from a file, (PEM)");
System.out.println("--sl <file> List of known suffixes.");
}
}