Certificates – Sign a JAR file using the XMSS Signature Scheme (PQC) with JarSigner

I'm trying to use JarSigner to sign .jar files with XMSS signatures.
With the BouncyCastle JCA / JCE Post-Quantum Cryptography Provider, it is possible to programmatically generate XMSS and XMSSMT KeyPairs (example).
To use JarSigner, it is important, as far as I know, to provide a KeyStore and the alias of the entry with which the code should be signed: jarsigner -keystore myKeystore -storetype JKS -storepass password -keypass password myjarfile.jar keystoreEntryAlias The KeyStore entry contains the Public / Secret KeyPair and its associated X.509 certificate.

The "normal" way to sign a Jar file with the JarSigner is as follows:

  1. Use keytool to generate the Public / Secret key pair and a certificate, and save it to a KeyStore (keytool -genkeypair -alias keystoreEntryAlias ​​-keyalg RSA -sigalg SHA256withRSA -dname CN = MyCompanyName -store-type JKS -keypass -key -key myoreystore.jks -storepass password)
  2. Use JarSigner to sign the JAR file with the SecretKey stored in mykeystore.jks with the alias keysotreEntryAlias ​​(jarsigner -keystore mykeystore.jks -store-type jks -storepass passeword -keypass-password myjarfile.jar keystoreEntryAlias)

To sign my file with an XMSS key, I theoretically have two options:

  1. Use BCPQC to programmatically create, save, and use XMSS KeyPairs in mykeystore jarsigner -keystore mykeystore -alias xmss via CLI to sign my file.
  2. Use the BCPQC provider with the keytool to generate XMSS KeyPairs directly from the CLI and store them in mykeystore (keytool would need 2 more arguments here: -providerclass org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider and -providerpath C: path to BouncyCastle provider bcprov-jdk15on-160.jarThen sign the file with JarSigner with the keystore entry

Unfortunately I have problems with both possibilities:

  1. Since I have not found a way to use JarSigner without CLI, I need to save my generated XMSS KeyPairs to a KeyStore. But I need the certificate that contains the public XMSS key. BouncyCastle provides an X.509CertificateBuilder that can be used to generate the required certificate. However, something seems to go wrong when you look at the generated certificate, especially signature algorithm and public key (source code below) [CertificateBuilderExample]with XMSSMT)
  2. It seems that keytool only uses the init (int) overload of BCPQCProvider and that XMSSKeyPairGeneratorSpi rejects it. It would like AlgorithmParameterSpec to be specially XMSSParameterSpec or not Init at all – and if I try the latter, a key pair is generated, but the resulting keys can not be encoded and thus not stored in the KeyStore.

My question would be:

Does anyone know of a way to sign .jar files with JarSigner using XMSS / XMSSMT, and can provide a more or less detailed explanation of what he / she did right that I did wrong?
Or if I was wrong about anything I mentioned above, make a correction and point out a way?

UPDATE 1: I am now able to work with another X509CertificateGenerator (source code below) [X509CertificateGenerator]) and to gather information from here, here and here Signing JAR files successfully programmatically with RSA from BouncyCastle (source code for signing below) [RSA_JarSigner]).

When I try to apply the same scheme that was used to sign with RSA for signing with XMSS or XMSSMT, I come up against it JarSignerException: Error in signer materialscaused by NoSuchAlgorithmException: Name of unrecognized algorithm: XMSS (Source code for XMSS / XMSSMT below [SignXMSS] [SignXMSSMT],

Hopefully someone can help me find out where the problem lies!

[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 dateunt to = "12-05-2020 10:20:56";
Date from = sdf.parse (datefrom);
Date to = sdf.parse (fileuntil);

// Create a 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 the certificate
ab, // start of validity
to // the end of the validity of the certificate
new X500Name ("CN = rootCA"), // name of the requestor of the certificate
rootCAKeyPair.getPublic ()); // public key of the certificate
// key 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 the signing authority, here it is self-signed
saveToFile (rootCA, "rootCA.cer");

}

private static keyPair generateKeyPair () returns 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 valid, String sigAlgName) raises 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 (serial number));
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 used.
X509CertImpl certificate = new X509CertImpl (info);
certificate.sign (privateKey, sigAlgName);

// Update the algorithm and step back.
sigAlgId = (AlgorithmId) certificate.get (X509CertImpl.SIG_ALG);
info.set (CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgId);
Certificate = new X509CertImpl (info);
certificate.sign (privateKey, sigAlgName);

return receipt
}

[RSA_JarSigner]

public class JarSignerTest {

public static void main (String[] args) throws exception {
JarSignerTest jst = new JarSignerTest ();
jst.SignRSA ();
}

public void SignRSA () throws an exception {
Security.addProvider (new BouncyCastleProvider ());
File inputFile = new file ("C: \ path \ to \ jar \ toSign \ jarfile.jar"),
Output file = 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 foo = Arrays.asList (string);
CertificateFactory certificateFactory = CertificateFactory.getInstance ("X.509");
CertPath certPath = certificateFactory.generateCertPath (foo);
JarSigner signer = new JarSigner.Builder (keyPair.getPrivate (), certPath)
.digestAlgorithm ("SHA-256")
.signatureAlgorithm ("SHA256withRSA")
.to build();
try (ZipFile in = new ZipFile (inputFile);
FileOutputStream out = new FileOutputStream (output file)) {
signer.sign (in, out);
}
}
}

[SignXMSS]

public void SignXMSS () throws an exception {
Security.addProvider (new BouncyCastlePQCProvider ());
File inputFile = new file ("C: \ path \ to \ jar \ toSign \ jarfile.jar"),
Output file = 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 foo = Arrays.asList (string);
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 ())
.to build();
try (ZipFile in = new ZipFile (inputFile);
FileOutputStream out = new FileOutputStream (output file)) {
signer.sign (in, out);
}
}

[SignXMSSMT]

public void SignXMSSMT () throws an exception {
Security.addProvider (new BouncyCastlePQCProvider ());
File inputFile = new file ("C: \ path \ to \ jar \ toSign \ jarfile.jar"),
Output file = 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 foo = Arrays.asList (string);
CertificateFactory certificateFactory = CertificateFactory.getInstance ("X.509");
CertPath certPath = certificateFactory.generateCertPath (foo);
JarSigner signer = new JarSigner.Builder (keyPair.getPrivate (), certPath)
.digestAlgorithm ("SHA-256")
.signatureAlgorithm ("SHA256withXMSSMT")
.to build();
try (ZipFile in = new ZipFile (inputFile);
FileOutputStream out = new FileOutputStream (output file)) {
signer.sign (in, out);
}
}