- removed milestone
ECDHDecrypter with unextractable private key from HSM
Hello all,
I'm using Java 1.8, Nimbus 6.0.1, an Utimaco HSM device and PKSC11 to generate/store keys to encrypt, decrypt, sign data, I already implemented encryption and signing using ECDHEncrypter and ECDSASigner.
I have a problem when I want to decryp data, the ECDHDecrypter requires a ECKey or a ECPrivateKey, but I can only get a reference to a key/PrivateKey from HSM which is unexctractable.
Could you please share an example of ECHDecrytper using an unextractable private key coming from an HSM device.
Thank you.
Comments (11)
-
reporter -
You need to configure the ECDHDecrypter with a java.security.Provider loaded from your HSM driver.
The steps can be inferred from the signing example:
https://connect2id.com/products/nimbus-jose-jwt/examples/pkcs11
-
reporter I've already checked that, your example only shows how to sign with a RSASSASigner to which you can just pass a PrivateKey (RSA key)
In my case, to create the ECDHDecrypter I need an ECKey or an ECPrivateKey first, but I can only get a reference to a Key/PrivateKey from HSM which is unexctractable.
Can you please share a tip/example on how to create a Nimbus ECHDecrytper using an unextractable private key coming from an HSM device.
I already configured the HSM and I'm getting the provider and the keys from it, here is what I'm doing:
PKCS11 Config:
name=CryptoServer library=PATH_TO_THE_PKCS11_LIBRARY attributes(*,CKO_PRIVATE_KEY,*) = { CKA_DERIVE = true CKA_SIGN = true CKA_DECRYPT = true }
This is my test class:
import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; import java.text.ParseException; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Collections; import java.util.Date; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWEDecrypter; import com.nimbusds.jose.JWEEncrypter; import com.nimbusds.jose.JWEHeader; import com.nimbusds.jose.JWEObject; import com.nimbusds.jose.Payload; import com.nimbusds.jose.crypto.ECDHDecrypter; import com.nimbusds.jose.crypto.ECDHEncrypter; import sun.security.pkcs11.SunPKCS11; public class JWEDecryptHSM { static String configName = "PATH_TO_THE_PKCS11_CONFIG"; static String subject = "CN=Company-123456"; static String signatureAlgorithm = "SHA256WithECDSA"; static char[] keyPassword = "1234".toCharArray(); public static void main(String[] args) { try { Provider provider = new SunPKCS11(configName); Security.addProvider(provider); System.out.println("Provider Name: " + provider.getName()); char[] pin = "1234".toCharArray(); KeyStore ks = KeyStore.getInstance("PKCS11", provider); ks.load(null, pin); // for (String alias : Collections.list(ks.aliases())) { // System.out.println(alias); // } String keyName = "PrivateTest"; // Generate the Keypair to encrypt the contract data KeyPair keyPair = generateKeyPair(provider); PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); System.out.println("PUB. KEY: " + publicKey); System.out.println("PRIV. KEY: " + privateKey); storeKey(ks, keyPair, keyName, provider); Key hsmPrivateKey = ks.getKey(keyName, keyPassword); Key hsmPublicKey = ks.getCertificate(keyName).getPublicKey(); System.out.println("HSM PUB. KEY: " + hsmPublicKey); System.out.println("HSM PRIV. KEY: " + hsmPrivateKey); // generateKeyAgreement(hsmPublicKey); // NIMBUS try { // Encrypt ECPublicKey ecPublicKey = (ECPublicKey) hsmPublicKey; JWEHeader.Builder builder = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES_A256KW, EncryptionMethod.A256CBC_HS512); builder.keyID("XXX"); JWEHeader jweHeader = builder.build(); Payload payload = new Payload("Hello nimbus...".getBytes()); JWEObject encryptedContainer = new JWEObject(jweHeader, payload); JWEEncrypter encrypter = new ECDHEncrypter(ecPublicKey); encryptedContainer.encrypt(encrypter); String encryptedData = encryptedContainer.serialize(); System.out.println("ENCRYPTED: " + encryptedData); // Decrypt JWEObject jwe = JWEObject.parse(encryptedData); JWEDecrypter decrypter = new ECDHDecrypter((ECPrivateKey) hsmPrivateKey); // <- Here is failing decrypter.getJCAContext().setProvider(provider); jwe.decrypt(decrypter); String decrypted = jwe.getPayload().toString(); System.out.println("DECRYPTED: " + decrypted); } catch (JOSEException | ParseException e) { e.printStackTrace(); } } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | UnrecoverableKeyException e) { e.printStackTrace(); } } public static void storeKey(KeyStore ks, KeyPair keyPair, String keyName, Provider provider) { try { X509Certificate[] chain = generateV3Certificate(keyPair, provider); ks.setKeyEntry(keyName, keyPair.getPrivate(), keyPassword, chain); ks.store(null); System.out.println("============================= Stored Keys ============================="); for (String alias : Collections.list(ks.aliases())) { System.out.println(alias); } } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { e.printStackTrace(); } } public static X509Certificate[] generateV3Certificate(KeyPair pair, Provider provider) { X509Certificate[] chain = null; try { X509Certificate certificate = null; X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder( new X500Name("C=DE, L=Wolfsburg, O=My CA Inc"), BigInteger.valueOf(System.currentTimeMillis()), Date.from(LocalDateTime.now().toInstant(ZoneOffset.UTC)), Date.from(LocalDateTime.now().plusDays(365).toInstant(ZoneOffset.UTC)), new X500Name(subject), SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(pair.getPublic().getEncoded())) ); ContentSigner sigGen = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); X509CertificateHolder holder = certificateGenerator.build(sigGen); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); certificate = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(holder.toASN1Structure().getEncoded())); chain = new X509Certificate[] { certificate }; } catch (OperatorCreationException | CertificateException | IOException e) { e.printStackTrace(); } return chain; } public static KeyPair generateKeyPair(Provider provider) { KeyPair keyPair = null; try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", provider); //"EC" ECGenParameterSpec ecParameterSpec = new ECGenParameterSpec("secp256r1"); //secp256r1 keyPairGenerator.initialize(ecParameterSpec); keyPair = keyPairGenerator.generateKeyPair(); } catch (NoSuchAlgorithmException exception) { exception.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } return keyPair; } }
Thank you.
-
reporter I already tried to create an ECKey like this:
ECKey ecKey = new ECKey.Builder(Curve.P_256, ecPublicKey).privateKey((PrivateKey) hsmPrivateKey).build();
But I'm getting this exception:
com.nimbusds.jose.JOSEException at com.nimbusds.jose.JWEObject.decrypt(JWEObject.java:429) at JWEDecryptHSM.main(JWEDecryptHSM.java:121) Caused by: java.lang.NullPointerException at com.nimbusds.jose.crypto.utils.ECChecks.isPointOnCurve(ECChecks.java:55) at com.nimbusds.jose.crypto.ECDHDecrypter.decrypt(ECDHDecrypter.java:219) at com.nimbusds.jose.JWEObject.decrypt(JWEObject.java:415) ... 1 more
Thank you.
-
- changed status to open
Hi,
The HSM based KeyStore can only return PrivateKey instances, not ECPrivateKey instances.
We'll update the API accordingly. Unfortunately we don't have an HSM to test that, so we'll rely on your input. We have an HSM for testing that does only RSA.
-
reporter Hello,
Exactly, that's the problem, and the point is Nimbus does not support this (yet).
If you need some support on this I'm glad to help, even if it's just for testing :)
You can find an Utimaco HSM simulator here.
I'll keep one eye on this, is a crucial part of our project.
Thank you,
-
- changed status to resolved
Added new PrivateKey constructor, the EC curve must be explicitly specced because cannot be inferred from the PrivateKey interface: fa37605c1dc62348286711871a9369a4754c5a2c
-
Pushed to Maven Central as v6.5 :)
-
reporter Awesome! That was fast, I'm going to test.
Thanks a lot.
-
You're welcome! Did it work with your HSM?
-
reporter Hi, I'm testing, now I'm able to create the ECDHDecrypter but is giving me an error while trying to do the key agreement, I think is a problem related to the HSM, still working on it.
Thanks!
- Log in to comment