Extract keyUsage extension from a certificate using bouncy castle - java

I am writing a small CA implementation. It is possible to bootstrap this CA from an existing certificate. Upon doing this, I want to verify that the input has the correct extensions:
private static final Set<String> REQUIRED_CA_EXTENSIONS = Set.of(
Extension.keyUsage.getId(),
Extension.subjectKeyIdentifier.getId());
private static void validateExtensions(final X509Certificate certificate) {
if (!CertificateExtensions.getAll(certificate).containsAll(REQUIRED_CA_EXTENSIONS)) {
throw new RuntimeException("Attempted to create a CA from a certificate without required extensions");
}
}
// Util method
public static Set<String> getAll(final X509Certificate certificate) {
final Set<String> extensions = new HashSet<>();
extensions.addAll(certificate.getCriticalExtensionOIDs());
extensions.addAll(certificate.getNonCriticalExtensionOIDs());
return extensions;
}
However, this only verifies the extensions are present. I also need to verify that the keyUsage extension contains both keyCertSign and cRLSign to be able to sign certificates.
How can I do this using bouncy castle and/or JCA?

There is a method available on a JCA X509Certificate to return the bits of the keyUsage extension, called simply getKeyUsage(). The individual bits of the key usage represented by boolean values in the returned array, per the Java documentation.
It is also possible to do a little more work using the Bouncycastle libraries to accomplish the same result. I show both methods because the Bouncycastle libraries offer much more complete support for examining an X509Certificate, so it's useful to have an example illustrating something easy in case you want to do something more difficult.
Note that this requires two Bouncycastle libraries as of this writing, the main provider/crypto library and also the PKIX/CMS library.
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509CertificateHolder;
import java.io.FileInputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class X509CheckKeyUsage {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("DST_X3_CA.pem"); // for example
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
// check with simple JCA methods
boolean [] keyUsageBools = cert.getKeyUsage();
final int KEY_CERT_SIGN = 5;
final int CRL_SIGN = 6;
boolean usagesVerified = keyUsageBools[KEY_CERT_SIGN] && keyUsageBools[CRL_SIGN];
System.out.println("key usage bits verified? " + usagesVerified);
// Convert the jca x.509 cert to a bouncycastle x.509 cert, in two steps
org.bouncycastle.asn1.x509.Certificate bcCert = org.bouncycastle.asn1.x509.Certificate
.getInstance(ASN1Primitive.fromByteArray(cert.getEncoded())); // step 1
X509CertificateHolder bcX509Cert = new X509CertificateHolder(bcCert); // step 2
// now verify keyUsage bits
final int requiredKeyUsageBits = KeyUsage.keyCertSign | KeyUsage.cRLSign;
usagesVerified = KeyUsage.fromExtensions(bcX509Cert.getExtensions()).hasUsages(requiredKeyUsageBits);
System.out.println("key usage bits verified? " + usagesVerified);
}
}

Related

Java - Storing Keypairs in KeyStore

I am implementing an Hybrid Encryption project and I have generated 2 key pairs for sender and receiver keys (private and public). I have these keys in a file.
KeyPair Generation Code
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import Decoder.BASE64Encoder;
public class GenerateRSAKeys{
public static void main(String[] args)
{
String publicKeyFilename = null;
String privateKeyFilename = null;
publicKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_publicKey";
privateKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_privateKey";
GenerateRSAKeys generateRSAKeys = new GenerateRSAKeys();
generateRSAKeys.generate(publicKeyFilename, privateKeyFilename);
}
private void generate (String publicKeyFilename, String privateFilename){
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Create the public and private keys
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
BASE64Encoder b64 = new BASE64Encoder();
SecureRandom random = createFixedRandom();
generator.initialize(1024, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("publicKey : " + b64.encode(pubKey.getEncoded()));
System.out.println("privateKey : " + b64.encode(privKey.getEncoded()));
BufferedWriter out = new BufferedWriter(new FileWriter(publicKeyFilename));
out.write(b64.encode(pubKey.getEncoded()));
out.close();
out = new BufferedWriter(new FileWriter(privateFilename));
out.write(b64.encode(privKey.getEncoded()));
out.close();
}
catch (Exception e) {
System.out.println(e);
}
}
public static SecureRandom createFixedRandom()
{
return new FixedRand();
}
private static class FixedRand extends SecureRandom {
MessageDigest sha;
byte[] state;
FixedRand() {
try
{
this.sha = MessageDigest.getInstance("SHA-1");
this.state = sha.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("can't find SHA-1!");
}
}
public void nextBytes(byte[] bytes){
int off = 0;
sha.update(state);
while (off < bytes.length)
{
state = sha.digest();
if (bytes.length - off > state.length)
{
System.arraycopy(state, 0, bytes, off, state.length);
}
else
{
System.arraycopy(state, 0, bytes, off, bytes.length - off);
}
off += state.length;
sha.update(state);
}
}
}
}
Now, I need to secure these keys (probably not on any disk directly).
I have searched for this case on the internet; saving keys into key stores is the way to secure keys and use the same keystore when reading keys for the use in encryption and decryption.
Can someone suggest me how to save public and private key in key store and how to read it in java?
The Java KeyStore API (and underlying providers) does not support storing a keypair -- that is, a privatekey and publickey (which can then be used for publickey operations like encrypt and verify). It does support storing a privatekey and a certificate chain for the publickey. The certificate chain can consist of only one certificate, especially if that certificate is a self-signed certificate (which by definition is a chain by itself). That is exactly what commandline keytool -genkeypair does: it generates a keypair and stores the privatekey plus a self-signed certificate for the publickey in the (specified or defaulted) keystore file.
If you were using only 'standard' JRE, creating a self-signed (or other) certificate in code is not very easy.
However, if you have BouncyCastle added, as you obviously do or the code you posted wouldn't work, that has numerous applicable capabilities. For a simple example of generating a cert with only bcprov (the old, deprecated X509V3CertificateGenerator) see my JAVA API to create a keystore and attaching a csr and keypair to it -- but do as I recommended there but didn't show: don't create the unneeded CSR first, instead create the cert using the desired name and publickey.
Better, if you also have or can get bcpkix, use X509v3CertificateBuilder. See examples at Self signed X509 Certificate with Bouncy Castle in Java and Generating X509 Certificate using Bouncy Castle Java .
For writing out, and reading back in, a keystore file containing this data, just see the Javadoc linked above. Note Java traditionally defaulted to JKS-format files, and you will find many older answers here and elsewhere on the web which assume that, but since Java 9 in 2017 it defaults to PKCS12-format, which is not only standard and thus (mostly) interoperable but also more secure (uses a much better PBKDF algorithm).
However, your code uses a completely bogus random generator, and as a result generates the same keypair every time. Given this, there is no real need to store it, since you can always re-generate it any time you want. In fact, there is no benefit to even having this keypairs since it provides no security at all.

Implement Diffie-Hellman key exchange in Java

I am trying to implement Diffie-Hellman key exchange in Java, but I'm having a hard time understanding the specification:
Complete the Diffie-Hellman key exchange process as a local mechanism
according to JWA (RFC 7518) in Direct Key Agreement mode using curve
P-256, dT and QC to produce a pair of CEKs (one for each direction)
which are identified by Transaction ID. The parameter values supported
in this version of the specification are:
“alg”: ECDH-ES
“apv”: SDK Reference Number
“epk”: QC, in JSON Web Key (JWK) format
{“kty”:”EC” “crv”:“P-256”}
All other parameters: not present
CEK: “kty”:oct - 256 bits
Create a JSON object of the following data as the JWS payload to be
signed:
{“MyPublicKey”: “QT”, “SDKPublicKey”:” QC”}
Generate a digital signature of the full JSON object according to JWS
(RFC 7515) using JWS Compact Serialization. The parameter values
supported in this version of the specification are:
“alg”: PS256 or ES256
“x5c”: X.5C v3: Cert(MyPb) plus optionally chaining certificates
From my understanding, ECDH will produce a secret key. After sharing my ephemeral public key (QT), the SDK produces the same secret key, so we can later exchange JWE messages encrypted with the same secret key.
The JSON {“MyPublicKey”: “QT”, “SDKPublicKey”:” QC”} will be signed and sent, but I do not understand how I will use apv and epk since these header params are used in JWE and not in the first JWS to be shared.
On the same specification, they talk about these JWE messages, but they do not have these apv and epk parameters.
Encrypt the JSON object according to JWE (RFC 7516) using the same
“enc” algorithm used by the SDK, the CEK obtained identified by “kid”
and JWE Compact Serialization. The parameter values supported in this
version of the specification are:
“alg”: dir
“enc”: either A128CBC-HS256 or A128GCM
“kid”: Transaction ID
All other parameters: not present
I also read the example in RFC 7518 where I can see the header params apv and epk being used but I'm not sure which header params, JWE's or JWS's ?
Any thought on how this could be implemented using nimbus-jose-jwt or any other java library would be really helpful. Thanks
Both apv (Agreement PartyVInfo) and epk (Ephemeral Public Key) are optional, so they can be used in multiple ways. You can use apv to reflect SDK version for example. They are added to the JWE header.
You can read more about JWE in Nimbus here
An example using Nimbus JOSE would be:
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.KeyOperation;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jose.util.Base64URL;
public class Security {
public void generateJWE() throws JOSEException, URISyntaxException {
JWEHeader jweHeader = buildJWEHeader(new Base64URL("202333517"), buildECKey());
JWEObject jweObject = new JWEObject(jweHeader, new Payload("Hello World!"));
}
private JWEHeader buildJWEHeader(Base64URL apv, ECKey epk) {
JWEHeader.Builder jweBuilder = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A128GCM);
jweBuilder.agreementPartyVInfo(apv);
jweBuilder.ephemeralPublicKey(epk);
return jweBuilder.build();
}
private ECKey buildECKey() throws URISyntaxException {
Set<KeyOperation> keyOperations = new HashSet<>();
keyOperations.add(KeyOperation.ENCRYPT);
String transactionID = "73024831";
URI x5u = new URI("https//website.certificate");
KeyStore keystore = null; //initialize it
List<Base64> x5c = new ArrayList<>();
return new ECKey(Curve.P_256, new Base64URL("x"), new Base64URL("y"), KeyUse.ENCRYPTION, keyOperations, Algorithm.NONE, transactionID, x5u, new Base64URL("x5t"), new Base64URL("x5t256"), x5c, keystore);
}
}
Instead of EncryptionMethod.A128GCM you can use EncryptionMethod.A128CBC-HS256 as in your specification. apv and epk are added to JWEHeader inside builder.
Other parameters can be chosen in constructors of JWEHeader.Builder and ECKey. I used ECDH-ES algorithm, A128GCM encryption method, P-256 curve (elliptic curve is default in ECKey generation),
transaction ID is a string. I chose other parameters without any clear pattern. Initialization of KeyStore would be too broad for the example. Encryption is only one thing you can do with JWE, among signature and others.
Nimbus (as well as Jose4j) is not the best choice for implementing the specification (I guess it's 3D secure 2.x.x).
These libraries do not return content encrypion key but use it to encrypt or decrypt messages as per JWE spec.
I found that Apache CXF Jose library does its job well as a JWE/JWS/JWK implementation. Except generating ephemeral key pairs. But it's can easily be done with Bouncy Castle:
Security.addProvider(new BouncyCastleProvider());
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec(JsonWebKey.EC_CURVE_P256);
KeyPairGenerator g = KeyPairGenerator.getInstance(ALGORITHM_SIGNATURE_EC, "BC");
g.initialize(ecGenSpec, new SecureRandom());
KeyPair keyPair = g.generateKeyPair();
Content encrypion key can be produced with this code:
byte[] cek = JweUtils.getECDHKey(privateKey, publicKey, null, SDKReferenceNumber, "", 256);
And used to encrypt messages getting JWE compact representation:
JweEncryption jweEncryption = JweUtils.getDirectKeyJweEncryption(cek, contentAlgorithm);
JweHeaders head = new JweHeaders();
head.setHeader(JoseConstants.HEADER_KEY_ID, keyId);
String encrypted = jweEncryption.encrypt(contentToEncrypt.getBytes(StandardCharsets.UTF_8), head);
Or decrypt:
JweCompactConsumer compactConsumer = new JweCompactConsumer(encrypted);
JweHeaders head = compactConsumer.getJweHeaders();
JweDecryption jweDecryption = JweUtils.getDirectKeyJweDecryption(cek, head.getContentEncryptionAlgorithm());
JweDecryptionOutput out = jweDecryption.decrypt(encrypted);
String decrypted = out.getContentText();
I able to generate the Secret Key for A128CBC-HS256. However still not succeed in A128GCM. Adding the working sample for A128CBC-HS256 with help nimbus-jose library. This below code is only for key generation.
Please also note that I override the nimbus-jose classes for my usage. Hope it helps.
private void generateSHA256SecretKey(AREQ areq, ECKey sdkPubKey, KeyPair acsKeyPair) throws Exception {
// Step 4 - Perform KeyAgreement and derive SecretKey
SecretKey Z = CustomECDH.deriveSharedSecret(sdkPubKey.toECPublicKey(), (ECPrivateKey)acsKeyPair.getPrivate(), null);
CustomConcatKDF concatKDF = new CustomConcatKDF("SHA-256");
String algIdString = "";
String partyVInfoString = areq.getSdkReferenceNumber();
int keylength = 256; //A128CBC-HS256
byte[] algID = CustomConcatKDF.encodeDataWithLength(algIdString.getBytes(StandardCharsets.UTF_8));
byte[] partyUInfo = CustomConcatKDF.encodeDataWithLength(new byte[0]);
byte[] partyVInfo = CustomConcatKDF.encodeDataWithLength(partyVInfoString.getBytes(StandardCharsets.UTF_8));
byte[] suppPubInfo = CustomConcatKDF.encodeIntData(keylength);
byte[] suppPrivInfo = CustomConcatKDF.encodeNoData();
SecretKey derivedKey = concatKDF.deriveKey(
Z,
keylength,
algID,
partyUInfo,
partyVInfo,
suppPubInfo,
suppPrivInfo);
System.out.println("Generated SHA256 DerivedKey : "+SecureUtils.bytesToHex(derivedKey.getEncoded()));
}

Reading elliptic curve private key from file with BouncyCastle

The BouncyCastle cryptography APIs allow for creating and verifying digital signatures using the regular java.security package objects, such as java.security.PublicKey, java.security.PrivateKey and their container java.security.KeyPair.
Suppose I use OpenSSL to create a .pem (or, if easier, a .der file) containing the elliptic curve private key I want to use in my application. For example, it looks like this:
-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK
oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun
GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==
-----END EC PRIVATE KEY-----
How do I use the BouncyCastle APIs to obtain a java.security.KeyPair containing both this private key and a corresponding public key?
Please note I want to use the APIs available in BouncyCastle 1.50 (which is current at the time of writing) and no deprecated APIs. This unfortunately excludes the PEMReader class used in other SO answers. Furthermore, this question is specific to the format of elliptic curves; they contain additional parameters when compared RSA or DSA key files.
In addition to the standard JCE approach shown by divanov as long as you give it the correct input (see my comment thereto), or just using JCE in the first place like your selfanswer, BouncyCastle 1.48 up DOES still contain the old PEMReader functionality just organized a bit differently and for this case you can use something like:
static void SO22963581BCPEMPrivateEC () throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Reader rdr = new StringReader ("-----BEGIN EC PRIVATE KEY-----\n"
+"MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK\n"
+"oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun\n"
+"GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==\n"+"-----END EC PRIVATE KEY-----\n");
Object parsed = new org.bouncycastle.openssl.PEMParser(rdr).readObject();
KeyPair pair = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().getKeyPair((org.bouncycastle.openssl.PEMKeyPair)parsed);
System.out.println (pair.getPrivate().getAlgorithm());
}
In Java this will be pretty much the same code. After striping guarding strings away and decoding Base64 data give it to this utility method:
public static PrivateKey keyToValue(byte[] pkcs8key)
throws GeneralSecurityException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
KeyFactory factory = KeyFactory.getInstance("ECDSA");
PrivateKey privateKey = factory.generatePrivate(spec);
return privateKey;
}
Since I only need this for a quick and dirty demo, I solved it in the following way (in Scala). First, I generate a public private key pair in the REPL and print out its data:
Security.addProvider(new BouncyCastleProvider)
val SignatureScheme = "some signature scheme, eg ECDSA"
val RandomAlgorithm = "some random algorithm, eg SHA1PRNG"
val keygen = KeyPairGenerator.getInstance(SignatureScheme)
val rng = SecureRandom.getInstance(RandomAlgorithm)
rng.setSeed(seed)
keygen.initialize(KeySize, rng)
val kp = keygen.generateKeyPair()
println(kp.getPublic.getEncoded.toSeq) // toSeq so that Scala actually prints it
println(kp.getPrivate.getEncoded.toSeq)
Then using the generated data,
val hardcodedPublic = Array[Byte]( /* data */ )
val hardcodedPrivate = Array[Byte]( /* data */ )
val factory = KeyFactory.getInstance(SignatureScheme)
val publicSpec = new X509EncodedKeySpec(hardcodedPublic)
val publicKey = factory.generatePublic(publicSpec)
val privateSpec = new PKCS8EncodedKeySpec(hardcodedPrivate)
val privateKey = factory.generatePrivate(privateSpec)
The key thing you need to know here is that by default public key data uses X509 encoding and private key data uses PKCS8 encoding. It should be possible to get OpenSSL to output these formats and parse them manually, but I did not check how.
I used information from this blog post about SpongyCastle (which is Android's BouncyCastle alias) quite helpful. It is unfortunate that documentation is fragmented like this, and that BouncyCastle's wiki was down at the time of this question.
Update: the BouncyCastle wiki is up, and you can find the documentation here.

Digital Signature SunMSCAPI provider & MS Crypto API

I want to sign file with the SunMSCAPI provider. As public key and signatures needs to be imported using MS Crypto API.
Generally generating signatures with SHA1withRSA, ends up with big-endian to little-endian (byte order) conversion.
//generate keystore with java keytool
$Keytool -genkey -alias tsign -keystore c:\test\tsignjks.p12 - keyalg rsa -storetype pkcs12
In Java application:
//for signing and getting keystore, assuming windows certificate is installed
..ks = KeyStore.getInstance("Windows-MY","SunMSCAPI");
PrivateKey priv = ks.getKey("tsign",password);
Signature rsa = Signature.getInstance("SHA1withRSA","SunMSCAPI");
rsa.initSign(priv);
..
rsa.update(buffer, 0, len);
..
byte[] realSig = rsa.sign();
//for writing public key for ms crypto api or exporting it from windows certificate store
Certificate cert = ks.getCertificate("tsign");
byte[] encodedCert = cert.getEncoded();
FileOutputStream certfos = new FileOutputStream("tsigncer.cer");
certfos.write(encodedCert);
//for writing signatures for ms crypto api
FileOutputStream sigfos = new FileOutputStream(targetPath + "/"
+ signatureName);
sigfos.write(realSig);
I believe that SunMSCAPI can resolve my problem, but I don't know when i import public key using MS Crypto API, It never import at at first stage (unless i change big endian to little endian byte order) below is my code for crypto API.
LPCSTR file = "tsigncer.cer";
//LPCSTR file = "omsign.p12";
BOOL crypt_res = FALSE;
HCRYPTPROV crypt_prov_hndl = NULL;
crypt_res = CryptAcquireContext(&crypt_prov_hndl, NULL, NULL, PROV_RSA_FULL, 0/*CRYPT_NEWKEYSET*/);
//crypt_res = CryptAcquireContext(&crypt_prov_hndl, NULL, NULL, PROV_DSS, CRYPT_VERIFYCONTEXT/*CRYPT_NEWKEYSET*/);
if (!crypt_res) {
HRESULT decode_hr = __HRESULT_FROM_WIN32(GetLastError());
return decode_hr;
}
// Load key file
HANDLE fileHandle = CreateFile(file, // name of the write
GENERIC_READ, // open for writing
0, // do not share
NULL, // default security
OPEN_EXISTING, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (fileHandle == INVALID_HANDLE_VALUE)
{
DWORD d = GetLastError();
return -1;
}
BYTE buffer[2056];
DWORD fileSize = 0;
DWORD fileSizeResult = GetFileSize(fileHandle, &fileSize);
DWORD numBytesRead = 0;
BOOL fileLoadResult = ReadFile(fileHandle, (PVOID)buffer, fileSizeResult, &numBytesRead, NULL);
// Import key
BOOL result = ImportKey(crypt_prov_hndl, (LPBYTE)buffer, numBytesRead);
//result is always false..
If you work with MSCAPI, it is assumed that you've added your key to the Microsoft Certificate store. You can check if the key is present by going to "Internet Properties" > "Content" > "Certificates" which gives you a list of certificates that are available. If your certificate isn't there, you can't use it. If it's there, you need this code:
SunMSCAPI providerMSCAPI = new SunMSCAPI();
Security.addProvider(providerMSCAPI);
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
From there on, the code is pretty standard. Please consult my book on digital signatures for more info (the book is free).
IMPORTANT ADDITION: I forgot to mention that SunMSCAPI isn't present in the 64-bit version of Java 6 (I don't know about Java 7). You can fix this by installing the 32-bit version.

How to create a X509 certificate using Java?

I want to create a X509 certificate using Java language and then extract public key from it.
I have searched the internet and found many code examples, but all of them have errors (unknown variable or unknown type) or have many warnings that say something like : "the method ... from type ... is deprecated " etc.
For example, why the following code doesn't work:
PublicKey pk;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
String PKstr = pk.toString();
InputStream PKstream = new ByteArrayInputStream(PKstr.getBytes());
X509Certificate pkcert = (X509Certificate)cf.generateCertificate(PKstream);
Can anyone show me how to create a certificate using pure Java or Bouncy Castle and then get a public key from that?
Thanks all.
For JDK versions <17, you can also generate a certificate using only JDK classes. The disadvantage is that you have to use two classes from the sun.security.x509 package.
The code would be:
KeyStore keyStore = ... // your keystore
// generate the certificate
// first parameter = Algorithm
// second parameter = signrature algorithm
// third parameter = the provider to use to generate the keys (may be null or
// use the constructor without provider)
CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA256WithRSA", null);
// generate it with 2048 bits
certGen.generate(2048);
// prepare the validity of the certificate
long validSecs = (long) 365 * 24 * 60 * 60; // valid for one year
// add the certificate information, currently only valid for one year.
X509Certificate cert = certGen.getSelfCertificate(
// enter your details according to your application
new X500Name("CN=My Application,O=My Organisation,L=My City,C=DE"), validSecs);
// set the certificate and the key in the keystore
keyStore.setKeyEntry(certAlias, certGen.getPrivateKey(), null,
new X509Certificate[] { cert });
Retrieve the private key from the key store to encrypt or decrypt data.
Based on the code is from http://www.pixelstech.net/article/1408524957-Generate-cetrificate-in-Java----3
Yes, with BouncyCastle, creating an X509 cert from 2 public keys (the key for the cert and the one for the CA) is done here.
I turn the resulting certificate into PEM here.
For JDK >= 17, this is how to do it with BouncyCastle. The code example is in Kotlin, but it works exactly the same way in Java:
val keyPair = KeyPairGenerator.getInstance("RSA").genKeyPair()
val subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.public.encoded)
val now = Instant.now()
val validFrom = Date.from(now)
val validTo = Date.from(now.plusSeconds(60L * 60 * 24 * 365))
val certBuilder = X509v3CertificateBuilder(
X500Name("CN=My Application,O=My Organisation,L=My City,C=DE"),
BigInteger.ONE,
validFrom,
validTo,
X500Name("CN=My Application,O=My Organisation,L=My City,C=DE"),
subPubKeyInfo
)
val signer = JcaContentSignerBuilder("SHA256WithRSA")
.setProvider(BouncyCastleProvider())
.build(keyPair.private)
val certificate = certBuilder.build(signer)
This is the gradledependency you need to pull:
implementation("org.bouncycastle:bcpkix-jdk18on:1.72")

Categories