Search code examples
javabouncycastleprivate-keypkcs#11

File Signing using BouncyCastle in a HSM Environement


I have used

public static void signContent(String cfgFilePath , String alias ,String tokenPassword , String inputFile , String outputFile){
            try {
                CMSSignedDataGenerator generator = new CMSSignedDataGenerator();

                Provider pkcs11 = Security.getProvider("SunPKCS11");
                pkcs11 = pkcs11.configure(cfgFilePath);

                KeyStore keyStore = KeyStore.getInstance("PKCS11",pkcs11);
                keyStore.load(null, tokenPassword.toCharArray());

                logger.log(Level.INFO,"Keystore size : "+keyStore.size());
                logger.log(Level.INFO, "certificate found for alias given : "+keyStore.getCertificate(alias));
                PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, tokenPassword.toCharArray());

                logger.log(Level.INFO,"pvt key :" + privateKey);
                logger.log(Level.INFO,"pvt key  algorithm: "+privateKey.getAlgorithm());// RSA
                logger.log(Level.INFO,"key format : "+privateKey.getFormat()); // prints null

                X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
                List certList = new ArrayList();
                certList.add(cert);

                Store certs = new JcaCertStore(certList);
                generator.addCertificates(certs);

                ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(privateKey);

                generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
                        new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha256Signer, cert));//NO I18N

                byte[] content = Files.readAllBytes(Paths.get(inputFile)); // Reading byte content from the input file

                CMSProcessableByteArray cmsData = new CMSProcessableByteArray(content);
                CMSSignedData signedData = generator.generate(cmsData, true);
                // Get the signed JSON as a byte array
                byte[] signedJson = signedData.getEncoded();

                Files.write(Paths.get(outputFile),signedJson);  // writing the signed content to the output file.
            }
            catch (Exception e) {
                logger.log(Level.SEVERE,"Exception Occurred while signing the input file : ## STACKTRACE ##");
                e.printStackTrace();
            }
    }

to sign a file for integrity verification.

But im getting error on

org.bouncycastle.operator.OperatorCreationException: cannot create signer: Supplied key (sun.security.pkcs11.P11Key$P11PrivateKey) is not a RSAPrivateKey instance
    at org.bouncycastle.operator.jcajce.JcaContentSignerBuilder.build(Unknown Source)
    at com.ems.silentupdate.FileSigningHandler.signContent(FileSigningHandler.java:85)
    at com.ems.silentupdate.FileSigningHandler.main(FileSigningHandler.java:115)
Caused by: java.security.InvalidKeyException: Supplied key (sun.security.pkcs11.P11Key$P11PrivateKey) is not a RSAPrivateKey instance
    at org.bouncycastle.jcajce.provider.asymmetric.rsa.DigestSignatureSpi.engineInitSign(Unknown Source)
    at java.base/java.security.Signature$Delegate.engineInitSign(Signature.java:1351)
    at java.base/java.security.Signature.initSign(Signature.java:636)
    ... 3 more

Whats the problem.. Im also new to HSM related works, if explaining how does HSM works will be appreciated. Could anyone explain how to sign a file with private key that is stored in a HSM format. Im using java 11 to sign , and bouncycastle version - bcpkix-jdk15on.jar , bcprov-jdk15on.jar


Solution

  • Issue : i have used SHA256WithRSA and internally BC converted it to uppercase and with my PKCS11 provider , it cant able to find the signature Algorithm . Thus, i have implemented a custom signer.

    here is the code.

    static ContentSigner getCustomSigner(String signatureAlgo,Provider prov,PrivateKey key) throws Exception {
            
    AlgorithmIdentifier signerAlgoIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgo);
            return new ContentSigner() {
                final Signature var2 = Signature.getInstance("SHA256withRSA",prov);
                @Override
                public AlgorithmIdentifier getAlgorithmIdentifier() {
                    return signerAlgoIdentifier;
                }
    
                @Override
                public OutputStream getOutputStream() {
                    try {
                        var2.initSign(key);
                    } catch (InvalidKeyException e) {
                        System.out.println("Exception while initializing sign : # STACK # "+e);
                    }
                    return OutputStreamFactory.createStream(var2);
                }
    
                @Override
                public byte[] getSignature() {
                    try {
                        return var2.sign();
                    } catch (SignatureException e) {
                        System.out.println("Exception in getSignature() : # STACK # "+e);
                    }
                    return new byte[0];
                }
            };
        }