Bouncy castle create a certificate from openssl csr in java - java

I generated a ca-certificate and a key with openssl.
openssl genrsa -aes256 -out $CANAME.key 4096
openssl req -x509 -new -nodes -key $CANAME.key -sha256 -days 1826 -out $CANAME.crt -subj '/CN=MyOrg Root CA/C=AT/ST=Vienna/L=Vienna/O=MyOrg'
Now I want to sign CSRs with those.
I found this question, but I can't use the accepted answer, because the class PKCS10CertificationRequestHolder seems not to exist anymore.
Based on the seconds answer I created this service.
public class SignService {
private PrivateKey privateKey;
private X509CertificateHolder certificateHolder;
public SignService(
String caPathKey,
String caCert,
String caKeyPassword
) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
loadPrivateKey(caPathKey, caKeyPassword);
PEMParser pemParser = new PEMParser(new FileReader(caCert));
this.certificateHolder = (X509CertificateHolder) pemParser.readObject();
}
private void loadPrivateKey(String caPathKey, String caKeyPassword) throws IOException {
PEMParser pemParser = new PEMParser(new FileReader(caPathKey));
Object object = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair kp;
if (object instanceof PEMEncryptedKeyPair)
{
// Encrypted key - we will use provided password
PEMEncryptedKeyPair ckp = (PEMEncryptedKeyPair) object;
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(caKeyPassword.toCharArray());
kp = converter.getKeyPair(ckp.decryptKeyPair(decProv));
}
else
{
// Unencrypted key - no password needed
PEMKeyPair ukp = (PEMKeyPair) object;
kp = converter.getKeyPair(ukp);
}
this.privateKey = kp.getPrivate();
}
public String signCRT(String crs_str) throws NoSuchProviderException, IOException, KeyStoreException, NoSuchAlgorithmException, OperatorCreationException, CMSException {
PemReader p = new PemReader(new StringReader(crs_str));
PemObject pemObject = p.readPemObject();
PKCS10CertificationRequest csr = new PKCS10CertificationRequest(pemObject.getContent());
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
X500Name issuer = certificateHolder.getIssuer();
BigInteger serial = new BigInteger(32, new SecureRandom());
Date from = new Date();
Date to = new Date(System.currentTimeMillis() + (365 * 86400000L));
X509v3CertificateBuilder certgen = new X509v3CertificateBuilder(issuer, serial, from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo());
certgen.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
certgen.addExtension(X509Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(csr.getSubjectPublicKeyInfo().getEncoded()));
certgen.addExtension(X509Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifier(new GeneralNames(new GeneralName(this.certificateHolder.getSubject())), certificateHolder.getSerialNumber()));
ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(PrivateKeyFactory.createKey(this.privateKey.getEncoded()));
X509CertificateHolder holder = certgen.build(signer);
byte[] certencoded = holder.toASN1Structure().getEncoded();
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
signer = new JcaContentSignerBuilder("SHA1withRSA").build(this.privateKey);
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer, this.certificateHolder));
generator.addCertificate(new X509CertificateHolder(certencoded));
generator.addCertificate(new X509CertificateHolder(this.certificateHolder.getEncoded()));
CMSTypedData content = new CMSProcessableByteArray(certencoded);
CMSSignedData signeddata = generator.generate(content, true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("-----BEGIN PKCS #7 SIGNED DATA-----\n".getBytes("ISO-8859-1"));
byte[] cert_encoded = Base64.encode(signeddata.getEncoded());
for (int i = 0; i < cert_encoded.length; i++) {
if (i > 0 && i % 63 == 0) {
out.write('\n');
}
out.write(cert_encoded[i]);
}
out.write("\n-----END PKCS #7 SIGNED DATA-----\n".getBytes("ISO-8859-1"));
out.close();
return new String(out.toByteArray(), "ISO-8859-1");
}
}
The method signCrt does not throw any exceptions and returns a string (I am not sure what).
I can't verify it or even show any information about the certificate.
This openssl command works with the same csr file, I want to do the same thing from java.
openssl x509 -req -in test.csr -CA $CANAME.crt -CAkey $CANAME.key -CAcreateserial -out openssl-signed.crt -days 730 -sha256
The certificate created with openssl is much smaller the of my java method (Don't know of this is a useful information).

The answer you linked tells you what the code returns: "a valid PEM-encoded signedData object containing a signed Certificate chain (of the type that can be imported by keytool)". Actually it's not quite valid: (1) it doesn't put any linebreaks in the base64 as PEM officially requires -- which you fixed, although you did it at 63 not 64 as standard; (2) it uses BEGIN/END PKCS #7 SIGNED DATA whereas the standard is BEGIN/END PKCS7 -- or in most cases BEGIN/END CMS because CMS is basically a superset of PKCS7. keytool ignores both of these flaws when reading a 'CA reply' (i.e. -importcert to an existing privateKey entry), but other software doesn't -- like BouncyCastle and OpenSSL. You already added line breaks; if you change the BEGIN/END labels to PKCS7 and put the result in a file you can read it with among other things
openssl pkcs7 -in $file -print_certs # add -text for expanded details
However, calling this 'the type that can be imported by keytool' implies it is the only type which is false. keytool can read PKCS7 containing certs -- either PEM or DER -- but it can also read 'bare' X.509 certificates in either PEM or DER, and that is usually more convenient. To do that, replace everything from CMSSignedDataGenerator onward with something like for DER:
Files.write(Paths.get("outputfile"), certencoded)
or for PEM since you have Bouncy:
try( PemWriter w = new PemWriter(new FileWriter("outputfile")) ){
w.writeObject(new PemObject("CERTIFICATE",certencoded));
}
or if you prefer to do it yourself:
try( Writer w = new FileWriter("outputfile") ){
w.write("-----BEGIN CERTIFICATE-----\r\n"
+ Base64.getMimeEncoder().encodeToString(certencoded)
// MimeEncoder does linebreaks at 76 by default which
// is close enough and less work than doing it by hand
+ "\r\n-----END CERTIFICATE-----\r\n");
}
Note: you may not need the \r -- PEM doesn't actually specify CRLF vs LF linebreaks -- but MimeEncoder uses them on the interior breaks and I like to be consistent.
This -- just the certificate -- is what your openssl x509 -req -CA* alternative produces, and that's why it's much smaller -- one cert is smaller than three certs plus some overhead.
Several more points:
PEMParser can handle CSR just fine; you don't need the PemReader+PemObject detour
using the entire SubjectPublicKeyInfo for the SubjectKeyIdentifier extension is wrong. OTOH nothing uses the SKI extension in a leaf cert anyway, so simplest to just delete this rather than fix it.
in certgen.build() if you use JcaContentSignerBuilder instead of the BcRSA variant, you can pass cakey directly without going through PrivateKeyFactory and the signature scheme directly without going through two AlgorithmIdentifierFinders -- as the /*cms-sd*/generator.addSignerInfoGenerator later in the code already does.
speaking of the signature scheme, don't use SHA1 for cert signatures (or most others). It was already deprecated in 2013 when that answer was written, and in 2017 was publicly broken for collision (see https://shattered.io) after which many systems either reject it entirely or nag mercilessly for such use, and some related ones like git. In fact many authorities and checklisters now prohibit it for any use, even though some (like HMAC and PBKDF) aren't actually broken. Your openssl x509 -req alternative used SHA256 which is fine*.
and lastly, issuing a cert is NOT 'sign[ing] {the|a} CSR'. You can just look at the cert contents and see it's not at all the same as the CSR or even the CSR body. You create (or generate as used in the Bouncy names) a cert and sign the cert -- in response to a CSR.
* at least for now; if and when quantum cryptanalysis works a lot of currently used schemes, including probably SHA256-RSA signature, will be in trouble and will have to be replaced by 'post-quantum' schemes which people are now working to develop. But if this happens, you'll see it on every news channel and site in existence.

Related

PEMParser bouncy castle read certificate and private key from PEM

I have created a PEMParser object to which I pass a PEM string containing chain certificate, encrypted private key etc. Which is the way to retrieve the decrypted private key and the certificate X509? I have tried with :
pemParser.readObject()
I am able to get a x509CertificateHolder (how to get the certificate from it?) and at the second call an encrypted privatekey info.
Thanks in advance.
assuming that your pem file has two entries first is cert and second is key.
another assumption is that key is of type pkcs8 and it is password protected.
first call to pemParser.readObject() is done with assumption that first entry in pem file is of x509 certificate
second call to pemParser.readObject() is done with assumption that second entry in pem file is of pkcs8 password protected key
variable certificate will contain the x509 certificate and variable finalKey will contain the private key
private void getCertAndKeyFromPemFile(String fileName, String keyPassword) throws Exception {
Security.addProvider(new BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new FileReader(fileName));
JcaX509CertificateConverter x509Converter = new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider());
X509Certificate certificate =x509Converter.getCertificate((X509CertificateHolder) pemParser.readObject());
PKCS8EncryptedPrivateKeyInfo privateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemParser.readObject();
InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword.toCharArray());
PrivateKey finalKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo.decryptPrivateKeyInfo(decryptorProvider));
}

java - How to generate PrivateKey and PublicKey starting from a keystore (.p12)

Generate some keys with OpenSSL, then encode them in Base64 and obtain them and try to generate them to validate the authentication with JWT. Here is the code and description of what happens to me
Generate with the following commands:
openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_key.der
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der
base64 –w 0 private_key.pem > private_key_base64_enc.txt
base64 –w 0 public_key.der > public_key_base64_enc.txt
I saved in my vault.keystore from wildfly: private_key_base64_enc.txt and public_key_base64_enc.txt
Then in my java class I write the following:
private void jwtSignedAuthentication(String token, PropName vaultBlockName) throws Exception
{
String rsa512Alias = vaultBlockName.getDefaultValue();
String rsa512pvt = VaultReader.getValue(rsa512Alias, "privateKey");
String rsa512pbc = VaultReader.getValue(rsa512Alias, "publicKey");
KeyFactory keyfatc = null;
PrivateKey privateKey = null;
PublicKey publicKey = null;
try {
keyfatc = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
logger.error(e);
}
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(new String(Base64.getDecoder().decode(rsa512pvt.getBytes()))));
String line;
while ((line = rdr.readLine()) != null) {
pkcs8Lines.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+","");
byte[] dataPvt = Base64.getDecoder().decode(pkcs8Pem.getBytes());
PKCS8EncodedKeySpec specPvt = new PKCS8EncodedKeySpec(dataPvt);
byte[] dataPbc = Base64.getDecoder().decode(rsa512pbc.getBytes());
StringBuilder publicLinesBuilder = new StringBuilder();
BufferedReader readerPlubKey = new BufferedReader(new StringReader(new String(dataPbc)));
String lineP;
while ((lineP = readerPlubKey.readLine()) != null) {
publicLinesBuilder.append(lineP);
}
String pubK = publicLinesBuilder.toString();
pubK = pubK.replace("-----BEGIN CERTIFICATE-----", "");
pubK = pubK.replace("-----END CERTIFICATE-----", "");
pubK = pubK.replaceAll("\\s+","");
X509EncodedKeySpec specPbc = new X509EncodedKeySpec(Base64.getDecoder().decode(pubK.getBytes()));
try {
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
} catch (InvalidKeySpecException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
// Creación de un verificador JWT
JWTVerifier verifier = JWT.require(algorithm).withIssuer(JWT_CLAIM_ISSUER).acceptLeeway(2).build();
UserContext userContext = new UserContext();
userContext.setUserName(JWT_CLAIM_ISSUER);
try {
// Decode JWT, verificación del token.
#SuppressWarnings("unused")
DecodedJWT decodeJwt = verifier.verify(token);
} catch (JWTDecodeException e) {
logger.error(e);
}
}
When I try to generate the keys I return null:
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
Anyone have any idea what happens with this. Thanks in advance
For generate my JWT:
public ResteasyWebTarget getClientWebAgent(String host, String blockName) throws KeyStoreException
{
ResteasyClient clientBuilder = new ResteasyClientBuilder().establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(5, TimeUnit.SECONDS).build();
ResteasyWebTarget target = clientBuilder.target(host);
KeyPair keys = null;
try {
keys = keyStore.getKeys();
/*logger.infov(new String(Base64.getEncoder().encode(keys.getPrivate().getEncoded())));
logger.infov("****PUBLIC KEY ******");
logger.infov(new String(keys.getPublic().getEncoded()));*/
} catch (IOException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) keys.getPublic(), (RSAPrivateKey) keys.getPrivate());
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "RS512");
headerClaims.put("typ", "JWT");
JWTCreator.Builder jwtCreator = JWT.create();
jwtCreator.withHeader(headerClaims);
jwtCreator.withIssuer(JWT_CLAIM_ISSUER);
jwtCreator.withIssuedAt(LocalDate.now().toDate());
jwtCreator.withExpiresAt(LocalDate.now().toDateTimeAtCurrentTime().plusSeconds(30).toDate());
String jwtToken = jwtCreator.sign(algorithm);
target.register(new BearerAuthenticator(jwtToken));
target.register(new LanguageHeaderToken(Locale.getDefault()));
return target;
}
Your 'public key' is actually a certificate (specifically an X.509 v1 or v3 certificate, depending on your openssl config), which contains a publickey but is different from a publickey -- and is in PEM format even though you have misleadingly named it .der -- and your privatekey is encrypted.
In addition to the approach of using a PKCS12, as Roberto validly proposes and is usually the simplest because it's only one file to manage and is still encrypted and thus more secure:
Java can handle an X.509 certificate, but you use a CertificateFactory.getInstance("X.509") and give it an InputStream instead of a KeyFactory and an X509EncodedKeySpec. CertificateFactory can handle either PEM or DER, unlike KeyFactory which can handle only DER, so you don't need the de-PEM (strip BEGIN/END/EOL and decode base64) parts.
standard Java cannot handle encrypted PKCS8 keys directly. If you can add a thirdparty library, BouncyCastle's bcpkix can do so; search the dozen or so existing Qs that use PEMParser (not PEMReader, that's the older version) and JceOpenSSLPKCS8DecryptorBuilder. Otherwise, you can add -nodes to your req -newkey -x509 command to generate an unencrypted privatekey file, which after you de-PEM it does work in KeyFactory with PKCS8EncodedKeySpec. (It's still spelled -nodes even though the encryption used without it hasn't been plain aka single DES for decades.) Using an unencrypted privatekey file of course means that any intruder or malware on your system that can read that file can get your privatekey, which in many situations is a risk.
finally, if you really want only the keypair and not a certificate, don't bother with req -newkey -x509. Instead use openssl genpkey to generate the privatekey, or the older but simpler openssl genrsa -nodes followed by (or piped to) openssl pkcs8 -topk8 -nocrypt to convert it to PKCS8-unencrypted format. Then use openssl pkey -pubout or the older openssl rsa -pubout to make a separate file with the publickey. Those commands can write (and read back where applicable) DER format instead of PEM; if you do that, your code doesn't need the de-PEM steps, you can just pass the binary file contents to KeyFactory. The risks for an unencrypted file are the same as above.
Maybe you are generating the keystore without assigning a valid alias, looking at your command you are not using the -name option.
The command should be like this:
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der -name "alias"
A smarter way to use the keys in java is by creating a KeyPair:
KeyPair loadKeyPair() throws Exception {
// Read keystore from resource folder
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource("keyStore.p12");
File file = new File(Objects.requireNonNull(resource).toURI());
char[] keyPass = "1234".toCharArray();
String alias = "alias";
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream is = new FileInputStream(file)) {
keystore.load(is, keyPass);
}
Key key = keystore.getKey(alias, keyPass);
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
return new KeyPair(publicKey, (PrivateKey) key);
}
return null;
}
Then extract RSAPublicKey and RSAPrivateKey keys from the KeyPair:
void loadKeys() throws Exception{
KeyPair keyPair = loadKeyPair();
if (null != keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
}
}
Hope it can be helpful and good luck with your Json Web Tokens! :-p

How to sign a .jar file using XMSS (PQC) Signature Scheme with JarSigner

I am trying to use JarSigner to sign .jar files with XMSS Signatures.
With the use of the JCA/JCE Post-Quantum Cryptography Provider from BouncyCastle it is possible to generate XMSS and XMSSMT KeyPairs programmatically (example).
In order to use JarSigner it is, as far as I know, crucial to provide a KeyStore and the alias of the entry one wants to sign its code with: jarsigner -keystore myKeystore -storetype JKS -storepass password -keypass password myjarfile.jar keystoreEntryAlias The KeyStore entry contains the Public/Secret KeyPair and the assosiated X.509 Certificate.
The 'normal' way of signing a Jar file with the JarSigner is as follows:
Use keytool to generate the Public/Secret KeyPair and a Certificate then store them in a KeyStore (keytool -genkeypair -alias keystoreEntryAlias -keyalg RSA -sigalg SHA256withRSA -dname CN=MyCompanyName -storetype JKS -keypass password -keystore mykeystore.jks -storepass password)
Use JarSigner to sign the .jar using the SecretKey stored in mykeystore.jks with the alias keysotreEntryAlias (jarsigner -keystore mykeystore.jks -storetype jks -storepass passeword -keypass password myjarfile.jar keystoreEntryAlias)
In order to Sign my file with an XMSS Key I have theoretically two possibilities:
Use BCPQC to create XMSS KeyPairs programmatically, store them in mykeystore and use jarsigner -keystore mykeystore -alias xmss via CLI to sign my file.
Use BCPQC-Provider with the keytool in order to directly via CLI generate XMSS KeyPairs and store them in mykeystore (keytool would here need 2 more arguments: -providerclass org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider and -providerpath C:\Path\to\BouncyCastle\provider\bcprov-jdk15on-160.jar) Then sign the file with the keystore entry using JarSigner
Sadly I run into problems with both possibilities:
As I have not found a way yet to use JarSigner without CLI I need to put my generated XMSS KeyPairs in a KeyStore, but therefor I need the Certificate which includes the Public XMSS Key. BouncyCastle does provide a X.509CertificateBuilder which can be used to generate the needed Certificate, however something seems to go wrong if you take a look at the generated Certificate, especially at Signature Algorithm and Public Key(Source code at bottom [CertificateBuilderExample], using XMSSMT)
It appears keytool only uses the init(int) overload from BCPQCProvider and XMSSKeyPairGeneratorSpi rejects that; it wants AlgorithmParameterSpec specifically XMSSParameterSpec, or no init at all -- and if I try the latter, it does generates a keypair, but the resulting keys can't be encoded and thus can't be stored in KeyStore.
My Question would now be:
Does anyone know of a way to sign .jar Files using XMSS / XMSSMT with JarSigner and can provide a more or less detailed explanation on what he/her did right that I did wrong?
Or if I was wrong about anything I mentioned above provide a correction and point out a way to do so ?
UPDATE 1: I am now able, with the use of another X509CertificateGenerator (Source code at bottom [X509CertificateGenerator]) and intel gathered from here, here and here, to successfully sign jar files programmatically with RSA provided from BouncyCastle (Source code for signing at bottom [RSA_JarSigner]).
If I try to apply the same scheme used to sign with RSA to sign with XMSS or XMSSMT I run into a JarSignerException: Error in signer materialscaused by NoSuchAlgorithmException: unrecognized algorithm name: XMSS (Source code for XMSS/XMSSMT at bottom [SignXMSS] [SignXMSSMT].
Hopefully someone can help me figure out where the problem is!
UPDATE 2: It seems the problem with the generated XMSS (or XMSSMT) certificates is due to the fact that the entry for the Signature Algorithm (which is SHA256withXMSS) is passed as an ASN1ObjectIdentifier which is unknown to the system yet. Thus I did some research to see if BouncyCastle does not by accident have an XMSS Certificate Generator laying around somewhere.. Bingo, here is one!
I shortened the code a bit and came up with 1 generator and 1 verifier (Source code at bottom [XMSSGen] [XMSSVer].
The generator tho gives me the same Certificates I already got with the other methods (such as [X509CertificateGenerator]).
The verifier sadly prompts me this ugly error: Exception in thread "main" java.security.spec.InvalidKeySpecException: java.lang.ClassCastException: org.bouncycastle.asn1.DLSequence cannot be cast to org.bouncycastle.asn1.ASN1Integer at org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi.engineGeneratePrivate(Unknown Source) at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:384) at BCXMSSCertificateVerifyer.verifyCertificate(BCXMSSCertificateVerifyer.java:32) at BCXMSSCertificateTester.main(BCXMSSCertificateTester.java:23)
Maybe someone has an idea where it comes from / how to fix it. In order to see if BC it self can work with its own created XMSS Certificates.
Edit: One problem with the verifier: PrivateKey privKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); why would we need a private Key to verify the Certificate ? Makes no sense - just remove it and it works ^^ (or at least should)
UPDATE 3: I finally managed to get the the verifier to work properly, so I am currently able to produce and verify XMSS Certificates. I am also able to store XMSS KeyPairs with a Certificate containing the PublicKey in a KeyStore. Though I am still not able to sign any .jar file with.
Now comes something a bit strange: I am able to sign things with the BC XMSS KeyPairs (of course I am, thats what they got made for) tho if I save them (or at least the PrivateKey, as he is required to sign stuff) and reload it afterwards to sign stuff again with it, it does not work. Neither if I store them in a KeyStore and retrieve them nor if I save the Key as encoded Bytes to a file and load them again. (If your interested in the code, just comment and I'll post it here)
My suggestion is: as the XMSS Signature Scheme requires a state (the state of the OTS already used) to be saved, that this state somehow cannot be retrieved from the PrivateKey as it is loaded again (whether from KeyStore or file doesnt matter) and thus cannot be used to sign something with.
[CertificateBuilderExample]
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Random;
public class App {
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastlePQCProvider());
SimpleDateFormat sdf = new SimpleDateFormat("dd-M-yyyy hh:mm:ss");
String datefrom = "12-08-2018 10:20:56";
String dateuntil = "12-05-2020 10:20:56";
Date from = sdf.parse(datefrom);
Date until = sdf.parse(dateuntil);
// Create self signed Root CA certificate
KeyPair rootCAKeyPair = generateKeyPair();
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
new X500Name("CN=rootCA"), // issuer authority
BigInteger.valueOf(new Random().nextInt()), //serial number of certificate
from, // start of validity
until, //end of certificate validity
new X500Name("CN=rootCA"), // subject name of certificate
rootCAKeyPair.getPublic()); // public key of certificate
// key usage restrictions
builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign));
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
X509Certificate rootCA = new JcaX509CertificateConverter().getCertificate(builder
.build(new JcaContentSignerBuilder("SHA256withXMSSMT").setProvider("BCPQC").
build(rootCAKeyPair.getPrivate()))); // private key of signing authority , here it is self signed
saveToFile(rootCA, "rootCA.cer");
}
private static KeyPair generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
kpGen.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA256), new SecureRandom());
KeyPair kp = kpGen.generateKeyPair();
System.out.print("Public key:" + Arrays.toString(kp.getPublic().getEncoded()));
return kp;
}
private static void saveToFile(X509Certificate certificate, String filePath) throws IOException, CertificateEncodingException {
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
fileOutputStream.write(certificate.getEncoded());
fileOutputStream.flush();
fileOutputStream.close();
}
}
[X509CertificateGenerator]
public X509Certificate generateCertificate(String dn, KeyPair keyPair, int validity, String sigAlgName) throws GeneralSecurityException, IOException {
PrivateKey privateKey = keyPair.getPrivate();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
Date to = new Date(from.getTime() + validity * 1000L * 24L * 60L * 60L);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger serialNumber = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(dn);
AlgorithmId sigAlgId = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber));
info.set(X509CertInfo.SUBJECT, owner);
info.set(X509CertInfo.ISSUER, owner);
info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(sigAlgId));
// Sign the cert to identify the algorithm that's used.
X509CertImpl certificate = new X509CertImpl(info);
certificate.sign(privateKey, sigAlgName);
// Update the algorith, and resign.
sigAlgId = (AlgorithmId) certificate.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgId);
certificate = new X509CertImpl(info);
certificate.sign(privateKey, sigAlgName);
return certificate;
}
[RSA_JarSigner]
public class JarSignerTest {
public static void main(String[] args) throws Exception{
JarSignerTest jst = new JarSignerTest();
jst.SignRSA();
}
public void SignRSA() throws Exception{
Security.addProvider(new BouncyCastleProvider());
File inputFile = new File("C:\\Path\\to\\jar\\toSign\\jarfile.jar"),
outputfile = new File("C:\\Path\\to\\signedJar\\jarfile.jar");
X509CertificateGen x509certgen = new X509CertificateGen();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
kpGen.initialize(1024, new SecureRandom());
KeyPair keyPair = kpGen.generateKeyPair();
Certificate[] chain = {x509certgen.generateCertificate("cn=Unknown", keyPair, 356, "SHA256withRSA")};
List<? extends Certificate> foo = Arrays.asList(chain);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
CertPath certPath = certificateFactory.generateCertPath(foo);
JarSigner signer = new JarSigner.Builder(keyPair.getPrivate(), certPath)
.digestAlgorithm("SHA-256")
.signatureAlgorithm("SHA256withRSA")
.build();
try (ZipFile in = new ZipFile(inputFile);
FileOutputStream out = new FileOutputStream(outputfile)){
signer.sign(in, out);
}
}
}
[SignXMSS]
public void SignXMSS() throws Exception{
Security.addProvider(new BouncyCastlePQCProvider());
File inputFile = new File("C:\\Path\\to\\jar\\toSign\\jarfile.jar"),
outputfile = new File("C:\\Path\\to\\signedJar\\jarfile.jar");
X509CertificateGen x509certgen = new X509CertificateGen();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XMSS", "BCPQC");
kpGen.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
KeyPair keyPair = kpGen.generateKeyPair();
Certificate[] chain = {x509certgen.generateCertificate("cn=Unknown", keyPair, 356, "SHA256withXMSS")};
List<? extends Certificate> foo = Arrays.asList(chain);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
CertPath certPath = certificateFactory.generateCertPath(foo);
JarSigner signer = new JarSigner.Builder(keyPair.getPrivate(), certPath)
.digestAlgorithm("SHA-256")
.signatureAlgorithm("SHA256withXMSS", new BouncyCastlePQCProvider())
.build();
try (ZipFile in = new ZipFile(inputFile);
FileOutputStream out = new FileOutputStream(outputfile)){
signer.sign(in, out);
}
}
[SignXMSSMT]
public void SignXMSSMT() throws Exception{
Security.addProvider(new BouncyCastlePQCProvider());
File inputFile = new File("C:\\Path\\to\\jar\\toSign\\jarfile.jar"),
outputfile = new File("C:\\Path\\to\\signedJar\\jarfile.jar");
X509CertificateGen x509certgen = new X509CertificateGen();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
kpGen.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA256), new SecureRandom());
KeyPair keyPair = kpGen.generateKeyPair();
Certificate[] chain = {x509certgen.generateCertificate("cn=Unknown", keyPair, 356, "SHA256withXMSSMT")};
List<? extends Certificate> foo = Arrays.asList(chain);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
CertPath certPath = certificateFactory.generateCertPath(foo);
JarSigner signer = new JarSigner.Builder(keyPair.getPrivate(), certPath)
.digestAlgorithm("SHA-256")
.signatureAlgorithm("SHA256withXMSSMT")
.build();
try (ZipFile in = new ZipFile(inputFile);
FileOutputStream out = new FileOutputStream(outputfile)){
signer.sign(in, out);
}
}
[XMSSGen]
public class BCXMSSCertificateGenerator {
public static X509Certificate generateCertificate(PrivateKey privKey, PublicKey pubKey, int duration, boolean isSelfSigned) throws Exception {
Provider BC = new BouncyCastleProvider();
//
// distinguished name table.
//
X500NameBuilder builder = createStdBuilder();
//
// create the certificate - version 3
//
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256withXMSS").setProvider("BCPQC").build(privKey);
X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(new X500Name("cn=Java"), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date((long)(System.currentTimeMillis() + duration*8.65*Math.pow(10,7))), builder.build(), pubKey);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
cert.checkValidity(new Date());
if (isSelfSigned) {
//
// check verifies in general
//
cert.verify(pubKey);
//
// check verifies with contained key
//
cert.verify(cert.getPublicKey());
}
ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
return (X509Certificate) fact.generateCertificate(bIn);
}
private static X500NameBuilder createStdBuilder() {
X500NameBuilder builder = new X500NameBuilder(RFC4519Style.INSTANCE);
builder.addRDN(RFC4519Style.c, "AU");
builder.addRDN(RFC4519Style.o, "The Legion of the Bouncy Castle");
builder.addRDN(RFC4519Style.l, "Melbourne");
builder.addRDN(RFC4519Style.st, "Victoria");
builder.addRDN(PKCSObjectIdentifiers.pkcs_9_at_emailAddress, "feedback-crypto#bouncycastle.org");
return builder;
}
}
[XMSSVer]
public class BCXMSSCertificateVerifyer {
public static boolean verifyCertificate(byte[] certBytes, String sigAlgorithm, byte[] keyBytes) throws Exception{
ByteArrayInputStream bIn;
bIn = new ByteArrayInputStream(certBytes);
CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
Certificate cert = fact.generateCertificate(bIn);
PublicKey k = cert.getPublicKey();
X509CertificateHolder certHldr = new X509CertificateHolder(certBytes);
certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BCPQC").build(k));
System.out.println(cert);
KeyFactory keyFactory = KeyFactory.getInstance(k.getAlgorithm(), "BCPQC");
PrivateKey privKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); // ERROR at this line
/*_______________________________________________________________________________________________________________*\
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.lang.ClassCastException: org.bouncycastle.asn1.DLSequence cannot be cast to org.bouncycastle.asn1.ASN1Integer
at org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi.engineGeneratePrivate(Unknown Source)
at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:384)
at BCXMSSCertificateVerifyer.verifyCertificate(BCXMSSCertificateVerifyer.java:32)
at BCXMSSCertificateTester.main(BCXMSSCertificateTester.java:23)
_________________________________________________________________________________________________________________
/* */
Signature signer = Signature.getInstance(sigAlgorithm, "BCPQC");
signer.initSign(privKey);
signer.update(certBytes);
byte[] sig = signer.sign();
signer.initVerify(cert);
signer.update(certBytes);
signer.verify(sig);
return true;
}
}
TL;DR: Signing .jar Files with PQC Signature schemes such as XMSS provided by BC using the built in JarSigner is not possible. However one can wright its one JarSigner which then can.
Although Oracle provides with JCA/JCE an out of the box integration for Crypto Provider such as BC those registered providers are not used from JarSigner neither for signing nor for verifying.
Considering the JarSigner: the supported algorithms are hard-coded and can thus not be extended.
The only way to use JarSigner with the BC Provider is to rebuild it entirely. However this is not recommended.
For those which are not afraid of hijacking jdk source code, your project has to "override" the following classes from the jdk:
AlgorithmId
Attributes
ContentInfo
EndEntityChecker
HttpTimestamper
InvalidJarIndexError
Jarentry
JarException
JarFile
JarIndex
JarInputStream
JarOutputStream
JarSigner
JarVerifier
JavaUtilJarAccess
JavaUtilJarAccessImpl
JavaUtilZipFileAccess
Main (jdk.jartool.sun.security.tools.jarsigner)
Manifest
ManifestEntryVerifier
Pack200
PKCS7
PKIXValidator
Resources
Resources_ja
Resources_zh_CN
SharedSecrets
SignatureFileVerifier
SignerInfo
SimpleValidator
TimestampedSigner
Timestamper
TimestampToken
TSResponse
Validator
VersionedStream
Therefor copy paste the original code into your project and remove the import of all classes which you hijack so your "custom" classes will be taken instead of the official ones.
Note: Most of the classes mentioned above can be found in the java.base module, although some are in the jdk.jartool module (such as the JarSigner itself).
After cloning the necessary part of the jdk to get the JarSigner to work you can finally move on implementing your BC provider and the support of the PQC Signature Schemes, especially XMSSMT and SPHINCS, as for the moment it seems like XMSS has some major problems.
Note that for the verification of a signed jar file the JarVerifier takes the file extension of the signature block file and checks rather if its .RSA, .DCA or .EC. So you will have to add .XMSS .XMSSMT .SPHINCS256 etc. You will also have to tell the PKCS7 class in the parseSignedData method to use a BC certificate generator.
There are a few other things to change as well (e.g. AlgorithmID) which I don't remember, but once you have done all the necessary steps, your JarSigner is able to sign and verify .jar files using BC in addition to using the "normal" RSA DCA and EC signing and verifying.
Sadly I will not be able to share the final Source Code with you, although I will try answer as much of your questions as possible.

Verifying detached xml signatures generated using openssl

I currently have been assigned the task to verify digital signatures generated using openssl , The files I recieve are basically two files one being the xml data and the other being the corresponding signature of that xml data , The key used to sign the data is RSA with an algorithm of SHA1 , the openssl command used is : openssl smime -sign -binary -in {datafile} -out {signaturefile} -outform der -inkey {privatekey} -signer {publickey}
where {datafile} = The input file, in either CSV or XML format, containing the data to be signed {signaturefile} = The output file containing the digital signature only in PKCS#7 format, same filename as datafile but with .sig extension {privatekey} = The private key part of the key to be used for signing {publickey} = The public key part of the key to be used for signing , I have written a class to verify these files but the result always returns a false meaning the verification has failed. below is the code i have written: Could someone please help me on how to verify openssl detached signatures using java?
public PublicKey pubTest(String path) throws Exception
{
FileInputStream fin = new FileInputStream(path);
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)f.generateCertificate(fin);
PublicKey pk = certificate.getPublicKey();
System.out.println(pk);
return pk;
}
public byte[] signature(String sigPath) throws Exception
{
FileInputStream sigfis = new FileInputStream(sigPath);
byte[] sigToVerify = new byte[sigfis.available()];
sigfis.read(sigToVerify);
sigfis.close();
System.out.println(sigToVerify.length);
return sigToVerify ;
}
public boolean verification(PublicKey pubKey , String dataPath , byte[] sigToVerify ) throws Exception, NoSuchProviderException ,SignatureException
{
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(pubKey);
FileInputStream datafis = new FileInputStream(dataPath);
BufferedInputStream bufin = new BufferedInputStream(datafis);
byte[] buffer = new byte[1024];
int len;
while (bufin.available() != 0)
{
len = bufin.read(buffer);
sig.update(buffer, 0, len);
}
System.out.println(buffer.length);
bufin.close();
boolean verifies = sig.verify(sigToVerify);
System.out.println("signature verifies: " + verifies);
return verifies ;
}
Late now but if anyone else comes across it:
openssl smime -sign by default generates an "indirect" detached signature, that is, one which does not contain the data in encapContentInfo but does use signedAttrs. See https://www.ietf.org/rfc/rfc3369.txt 5.3 through 5.6; you need to compare a hash of the data to the message-digest attribute in the signedAttrs, and then verify the signature against the signedAttrs (with the IMPLICIT tagging reverted to basic).
Standard Java crypto does not (currently?) do CMS/PKCS#7 but BouncyCastle does if that's an option. Consider Correct way to sign and verify signature using bouncycastle .

Correctly creating a new certificate with an intermediate certificate using bouny castle

So my problem is as follows,
Basically I want to create a certificate chain using bouncy castle (jdk16 version 1.46). I am rather new to bouncy castle and java.security in general so if my approach might be completely wrong, but anyway this is what I did:
So far I am able to create a self signed certificate which I use as the root certificate. This is done using the following code:
//-----create CA certificate with key
KeyPair caPair = Signing.generateKeyPair("DSA", 1024, null, null);
This basically creates the keypair, the two null options are for a provider and a secure random, if needed.
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> caMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
caMap.put(X509Extensions.BasicConstraints, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(true, new BasicConstraints(true)));
//------this creates the self signed certificate
X509Certificate caCert = X509CertificateGenerator.generateX509Certificate(serial, "CN=CA", "CN=CA", start, end, "SHA1withDSA", caPair.getPrivate(), caPair.getPublic(), null, caMap);
This will create the a certificate with the provided attributes.
serial = simply the current time in milliseconds
start = same as serial basically (may have 1 or 2 milliseconds difference)
end = start + 2 days
The map simply adds the basic contraint to set the certificate to be a CA. I use a map here since I want to be able to add additional X509Extensions if need be.
//-----save ca certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter);
This will store the certificate and private key in a pem file using the bouncy caste pem writer.
After that the file is generated and I can install the file as well (I use IE and then install it via the Internet Options as a trusted CA. The certificate is also shown to be valid).
After that I create the intermediate certificate, using the following code (note the above code is in the same scope so those variables are available as well)
KeyPair intermediatePair = Signing.generateKeyPair("DSA", 1024, null, null);
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> intermediateMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
intermediateMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(caCert)));
intermediateMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(intermediatePair.getPublic())));
X509Certificate intermediateCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=intermediate", caCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", caPair.getPrivate(), intermediatePair.getPublic(), null, intermediateMap);
//-----save intermediate certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(intermediateCert, intermediatePair.getPrivate(), intermediateWriter);
The procedure is bascially the same, however I add additional X509Extensions:
X509Extensions.AuthorityKeyIdentifier = sets the CA certificate as the intermediates parent
X509Extensions.SubjectKeyIdentifier = uses the generates public key for the certificate
furthermore the CA is used as the issuer and the CA private key is used to create the intermediate certificate.
This also works and I can install the intermediate certificate (using IE again), it is also shown that the parent certififcate is the generated CA certificate and that the certificate is valid.
Now comes the tricky part where I am making a mistake I guess. I now create a new certificate using the intermediate certificate, using the following code.
KeyPair endPair = Signing.generateKeyPair("DSA", 1024, null, null);
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> endMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
endMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(intermediateCert)));
endMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(endPair.getPublic())));
X509Certificate endCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=end", intermediateCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", intermediatePair.getPrivate(), endPair.getPublic(), null, endMap);
X509CertificateGenerator.savePemX509Certificate(endCert, endPair.getPrivate(), endWriter);
Essentially it is the same as creating the intermediate certificate. However I now use the following X509Extension settings:
X509Extensions.AuthorityKeyIdentifier = sets the intermediate certificate as the certificates parent
X509Extensions.SubjectKeyIdentifier = uses the generates public key for the certificate
Also the intermediate certificate is used as the issuer and its private key is used to create the certificate.
I can also install the new certificate but when I examine if (again IE), it shows that the certificate is however invalid because "This CA is either not entitled to issue certificates or the certificate can not be used as an end-entity."
So I somehow need to enable the intermediate certificate to be able to create new certificates as well, by adding some KeyUsages/ExtendedKeyUsage I assume.
Does someone know how I enable the intermediate certificate to do what I need it to do or if I do something wrong in general ?
EDIT 1:
So okay I forgot to provide the code for the method which created the certificate and the one that saved it in PEM format (I renamed it to savePemX509Certificate since the old one was misguiding).
Code for the certificate generation:
public static X509Certificate generateX509Certificate(BigInteger serialnumber, String subject, String issuer, Date start , Date end, String signAlgorithm, PrivateKey privateKey, PublicKey publicKey, String provider, Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> map) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException
{
if(serialnumber!=null && subject!=null && issuer!=null && start!=null && end!=null && signAlgorithm !=null && privateKey!=null && publicKey!=null)
{
//-----GENERATE THE X509 CERTIFICATE
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X509Principal dnSubject = new X509Principal(subject);
X509Principal dnIssuer = new X509Principal(issuer);
certGen.setSerialNumber(serialnumber);
certGen.setSubjectDN(dnSubject);
certGen.setIssuerDN(dnIssuer);
certGen.setNotBefore(start);
certGen.setNotAfter(end);
certGen.setPublicKey(publicKey);
certGen.setSignatureAlgorithm(signAlgorithm);
//-----insert extension if needed
if(map!=null)
for(ASN1ObjectIdentifier extension : map.keySet())
certGen.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
return certGen.generate(privateKey, provider);
}
return null;
}
Code for the saveing of the certificate and key:
public static boolean savePemX509Certificate(X509Certificate cert, PrivateKey key, Writer writer) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException, IOException
{
if(cert!=null && key!=null && writer!=null)
{
PEMWriter pemWriter = new PEMWriter(writer);
pemWriter.writeObject(cert);
pemWriter.flush();
if(key!=null)
{
pemWriter.writeObject(key);
pemWriter.flush();
}
pemWriter.close();
return true;
}
return false;
}
As you can see I basically put the certificate and the key in the file, thats all. The result is the following and seems good to me.
-----BEGIN CERTIFICATE-----
MIICdjCCAjagAwIBAgIGAUDuXLRLMAkGByqGSM44BAMwDTELMAkGA1UEAwwCQ0Ew
HhcNMTMwOTA1MTM0MzA3WhcNMTMwOTA3MTM0MzA3WjANMQswCQYDVQQDDAJDQTCC
AbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
nwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgAeFoGATLbIr8+QNuxcbYJ7RhbefKWSC
Br67Pp4Ynikxx8FZN4kCjGX7pwT1KffN3gta7jxIXNM5G3IFbs4XnYljh5TbdnjP
9Ge3kxpwncsbMQfCqIwHh8T5gh55KaxH7yYV2mrtEEqj7NBL4thQhJe2WGwgkB9U
NxNmLoMq3m4poyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAJ
BgcqhkjOOAQDAy8AMCwCFFm5ybLY09y8y2uGsEnpceffy2KaAhQIyshgy3ohCLxQ
q3CmnvC+cfT2VQ==
-----END CERTIFICATE-----
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
TDv+z0kqAoGAB4WgYBMtsivz5A27FxtgntGFt58pZIIGvrs+nhieKTHHwVk3iQKM
ZfunBPUp983eC1ruPEhc0zkbcgVuzhediWOHlNt2eM/0Z7eTGnCdyxsxB8KojAeH
xPmCHnkprEfvJhXaau0QSqPs0Evi2FCEl7ZYbCCQH1Q3E2YugyrebikCFDJCJHtt
NWB4LWYc4y4QvJ/l46ap
-----END DSA PRIVATE KEY-----
So after gtrig provided me with the correct way to create the certificate, I ended up using this method to create either a normal or self signed (if the private key is from the same keyPair as the public key that is) certificate
public static X509Certificate createX509V3Certificate(X500Principal name, BigInteger serial, Date start, Date end, PublicKey pubKey, String algorithm, PrivateKey privateKey, Map<ASN1ObjectIdentifier, Entry<Boolean, ASN1Object>> map, X509Certificate parentCert) throws IOException, OperatorCreationException, CertificateException
{
if(serial!=null && start!=null && end!=null && name!=null && pubKey!=null && algorithm!=null && privateKey!=null)
{
ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(privateKey);
X509v3CertificateBuilder certBldr = null;
if(parentCert==null)
certBldr = new JcaX509v3CertificateBuilder(name, serial, start, end, name, pubKey);
else
certBldr = new JcaX509v3CertificateBuilder(parentCert, serial, start, end, name, pubKey);
if(map!=null)
for(ASN1ObjectIdentifier extension : map.keySet())
certBldr.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBldr.build(signer));
}
return null;
}
Something looks wrong with the way you're creating the PEM files. You're using a method called, generateSelfSignedPemX509Certificate, but you don't really want a self-signed certificate, you want an end certificate signed by the intermediate private key, and you want an intermediate certificate signed by the CA private key.
Also, you need basic constraints and key usage extensions on your certificates.
For creating certificates signed by other entities (non-self-signed), I use these methods from Bouncy Castle to create an "end" certificate.
ASN1Sequence seq=
(ASN1Sequence) new ASN1InputStream(parentPubKey.getEncoded()).readObject();
SubjectPublicKeyInfo parentPubKeyInfo = new SubjectPublicKeyInfo(seq);
ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(parentPrivKey);
X509v3CertificateBuilder certBldr =
new JcaX509v3CertificateBuilder(
parentCert,
serialNum,
startDate,
endDate,
distName,
pubKey)
.addExtension(
new ASN1ObjectIdentifier("2.5.29.35"),
false,
new AuthorityKeyIdentifier(parentPubKeyInfo))
.addExtension(
new ASN1ObjectIdentifier("2.5.29.19"),
false,
new BasicConstraints(false)) // true if it is allowed to sign other certs
.addExtension(
new ASN1ObjectIdentifier("2.5.29.15"),
true,
new X509KeyUsage(
X509KeyUsage.digitalSignature |
X509KeyUsage.nonRepudiation |
X509KeyUsage.keyEncipherment |
X509KeyUsage.dataEncipherment));
// Build/sign the certificate.
X509CertificateHolder certHolder = certBldr.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
.getCertificate(certHolder);
For a CA or intermediate certificate, you'll need to add a SubjectKeyIdentifier extension. Also, BasicConstraints should be true, and KeyUsage should be:
new X509KeyUsage(
X509KeyUsage.keyCertSign|
X509KeyUsage.cRLSign));

Categories

Resources