Java use SecureRandom with SunPKCS11 provider - java

I would like to use my PKCS#11 enabled device as a source of SecureRandom.
So I have done the following:
Provider pkcs11provider = new sun.security.pkcs11.SunPKCS11(pkcs11config);
Security.addProvider(pkcs11provider);
byte[] rb = new byte[100];
SecureRandom sr = SecureRandom.getInstance("PKCS11", pkcs11provider);
sr.nextBytes(rb);
And I always get an exception:
Exception in thread "main" java.security.NoSuchAlgorithmException: no such algorithm: PKCS11 for provider SunPKCS11-HSM
at sun.security.jca.GetInstance.getService(GetInstance.java:101)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:218)
at java.security.SecureRandom.getInstance(SecureRandom.java:383)
What I am doing wrong? According JDK PKCS#11 Reference Guide "PKCS11" should be supported algorithm for SecureRandom.

"PKCS11" doesn't sound like an algorithm name. It is the provider name. A provider can have their own algorithm names for specific crypto operations. To see what all algorithms they have, you can run this code snippet to see them.
Set<Provider.Service> services = pkcs11provider.getServices();
services.forEach(service ->
{
// System.out.println(service.getType()); // --> Look for 'SecureRandom' type
System.out.println(service.getAlgorithm());
});
Look for 'SecureRandom' type, and that's the algorithm you have to pass in as the first argument in SecureRandom.getInstance(.., ..).

Related

How to initialize the PKCS11 provider without using SunPKCS11?

I have a problem and I tried searching a solution but couldn't find it. Someone please help me, thanks you so much.
String pkcs11Config = "name=CA-Token\nlibrary=C:/java/CA-Token_v2.dll\nslot=1";
InputStream is = new ByteArrayInputStream(pkcs11Config.getBytes())
SunPKCS11 providerPKCS11 = new sun.security.pkcs11.SunPKCS11(is); // (1)
Security.addProvider(providerPKCS11);
For this code, can we initialize the PKCS11 provider without using SunPKCS11?
It's been a while since this question was posted, but if you'd like to use a PKCS11 provider other than SunPKCS11, you can use:
IAIK PKCS11 wrapper https://jce.iaik.tugraz.at/products/core-crypto-toolkits/pkcs11-wrapper/
OpenSC https://github.com/OpenSC/OpenSC-Java/blob/master/pkcs11-test/src/test/java/org/opensc/test/pkcs11/PKCS11ProviderTestCase.java
Build your own provider by following Oracle tutorial https://docs.oracle.com/en/java/javase/17/security/howtoimplaprovider.html#GUID-C485394F-08C9-4D35-A245-1B82CDDBC031
You just need to instantiate them and register with "Security.addProvider(yourProvider)"

ECDSA KeyPairGenerator not available (but in JUnit works)

I am getting below Exception which I observed when running Android app - it does not occurs when I run the code below as JUnit.
java.security.NoSuchAlgorithmException: ECDSA KeyPairGenerator not available
at java.security.KeyPairGenerator.getInstance(KeyPairGenerator.java:225)
at com.mhamdaoui.smartcardreader.CryptoUtils$Companion.getMerchantEphemeralPublicKey(CryptoUtils.kt:48)
at com.mhamdaoui.smartcardreader.MainActivity.onTagDiscovered(MainActivity.kt:80)
at android.nfc.NfcActivityManager.onTagDiscovered(NfcActivityManager.java:603)
at android.nfc.IAppCallback$Stub.onTransact(IAppCallback.java:83)
at android.os.Binder.execTransact(Binder.java:573)
The code:
Security.addProvider(BouncyCastleProvider())
val generator = KeyPairGenerator.getInstance("ECDSA")
val ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1")
generator.initialize(ecSpec)
val keyPair = generator.generateKeyPair()
val publicKey = keyPair.public as ECPublicKey
return publicKey.q.getEncoded(true)
How to resolve this issue?
UPDATE
When I am creating JUnit in test (I am using Android Studio):
#Test
fun compressedGeneratorTest() {
Security.addProvider(BouncyCastleProvider())
val generator = KeyPairGenerator.getInstance("ECDSA")
val ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1")
generator.initialize(ecSpec)
val keyPair = generator.generateKeyPair()
val publicKey = keyPair.public as ECPublicKey
val encoded = publicKey.q.getEncoded(true)
assert(true)
}
Everything works also. How to resolve this issue on App runtime?
The problem is that it is not possible to use BounceyCastle on Android - instead use SpongyCastle:
implementation 'com.madgag.spongycastle:prov:1.54.0.0'
implementation 'com.madgag.spongycastle:pkix:1.54.0.0'
And then initialize the provider with BouncyCastleProvider instance like below:
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider())
Latest version of BouncyCastle libraries can be used to generate keypairs using ECDSA algorithm on Android device with the below mentioned steps.
Include the latest bouncy castle library by adding the below dependencies.
implementation "org.bouncycastle:bcprov-jdk15to18:1.68"
implementation "org.bouncycastle:bcpkix-jdk15to18:1.68"
Note: Refer Provider, PKIX to get the latest version details.
After adding the libraries, follow any of the below mentioned approach based on the mentioned need.
Approach 1
If you want to use the provider entire application level, replace the Android OS Bouncycastle provider with the provider from the added library using below line.
// Remove the OS provided bouncy castle provider
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
// Add the bouncy castle provider from the added library
Security.addProvider(org.bouncycastle.jce.provider.BouncyCastleProvider())
Approach 2
If you don't want to replace the Provider throughout the application, you can pass the provider instance to the KeyPairGenerator like below.
Java
KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", new org.bouncycastle.jce.provider.BouncyCastleProvider())
Kotlin
val generator = KeyPairGenerator.getInstance("ECDSA", org.bouncycastle.jce.provider.BouncyCastleProvider())
You can use
implementation("org.bouncycastle:bcprov-jdk15on:1.70")

Bouncy Castle Getting FipsSecureRandom

I'm trying to use Bouncy Castle in approved mode (CryptoServicesRegistrar.SetApprovedOnlyMode(true);) and am having some trouble generating RSA keys. I keep throwing exceptions on the following line.
var generator = CryptoServicesRegistrar.CreateGenerator(new FipsRsa.KeyGenerationParameters(BigInteger.ValueOf(65537L), 3072), FipsSecureRandom.GetInstance(FipsDrbg.CtrAes256.Algorithm.Name));
I have debugged the constructor, looked at the source code, and so on, but I can't figure out how to get an FSR object. All the builder methods for the object are internal to the BC library so I can't get to them. The methods provided in the FipsDrbg class are all internal as well and the provided stubs (such as CtrAes256 in my example) don't expose it either. I've tried passing a string of a PRNG method directly to the constructor from the values I've found in the documentation such as "SHA256PRNG." This gives the following exception:
attempt to create key pair with unapproved RNG: RSA
Looking in the source, the method throwing this exception comes from a ValidateRandom method that checks to make sure the random instance is a FipsSecureRandom so it doesn't get me anywhere.
Lastly, trying to backtrack the Builder : IDrbgBuilder<FipsSecureRandom> class doesn't lead anywhere. They're wholly used in the Base : Org.BouncyCastle.Crypto.Parameters.Parameters<FipsAlgorithm>, IDrbgBuilderService<FipsSecureRandom> class which is completely internal. While the Builder's Build method is public, it's not static and the constructor is internal so I can't create an instance of it to build anything.
What am I missing?
Update:
I was asked to split out the failing line so here is that line split.
var rsaParams = new FipsRsa.KeyGenerationParameters(BigInteger.ValueOf(65537L), 3072);
var fipsRandom = FipsSecureRandom.GetInstance("SHA256PRNG");
var generator = CryptoServicesRegistrar.CreateGenerator(rsaParams, fipsRandom);
The line throwing the exception is the var generator = line. The full exception details are such.
Source: "bc-fips-1.0.1"
Message: attempt to create key pair with unapproved RNG: RSA
at Org.BouncyCastle.Crypto.Fips.Utils.ValidateRandom(SecureRandom random, Int32 securityStrength, FipsAlgorithm algorithm, String message)
at Org.BouncyCastle.Crypto.Fips.FipsRsa.KeyPairGenerator..ctor(KeyGenerationParameters keyGenParameters, SecureRandom random)
at Org.BouncyCastle.Crypto.Fips.FipsRsa.KeyGenerationParameters.<>c.<Org.BouncyCastle.Crypto.IGenerationService<Org.BouncyCastle.Crypto.Fips.FipsRsa.KeyPairGenerator>.GetFunc>b__17_0(IParameters`1 parameters, SecureRandom random)
at Org.BouncyCastle.Security.SecurityContext.CreateGenerator[A](IGenerationServiceType`1 type, SecureRandom random)
at Org.BouncyCastle.Crypto.CryptoServicesRegistrar.CreateGenerator[A](IGenerationServiceType`1 type, SecureRandom random)
No inner exception, no exception data.

java pkcs11 slot detection issue with OpenSC if smartcard is removed and inserted again

Background information Which might help in alaysis :
From a web application i am trying to connect to smart card and read certificates from a java program which runs on client machine to perform some signing operation.
I am using Opensc-PKCS11.dll with java sunpkcs11 provider class to access certificate on the smart card (FIPS PIV compliance smart card).
My problem is as long as smart card is connected i am able access keystore on smart card and perform crypto operations, but when we remove smart card and insert again, program not able to fetch slot id due to which loading of provider fails.
Since i cannot hard code my slot id i am leaving it as either 0/-1
Config file content
Name="Opensc"
Library="OpenSC-PKCS11.dll"
slot=-1
showinfo=true
byte[] pkcs11configBytes = configName.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11configBytes);
bc = new org.bouncycastle.jce.provider.BouncyCastleProvider();
Security.addProvider(bc);
sun = new sun.security.pkcs11.SunPKCS11(confStream);
Security.addProvider(sun);
This question does provide enough information though related.
Java - how to detect smartcard hotplug
Update: I could fix the issue. In finally block I invoked C_Finalize from provider after my job with provider is finished. For the next run in the same java instance i did some thing like below clearing PKCS11 map and initialize provider again
Field moduleMapField = PKCS11.class.getDeclaredField("moduleMap");
moduleMapField.setAccessible(true);
Map<?, ?> moduleMap = (Map<?, ?>) moduleMapField.get(null);
moduleMap.clear(); // force re-execution of C_Initialize next time
//load PKCS#11
Method getInstanceMethod = PKCS11.class.getMethod("getInstance",
String.class, String.class, CK_C_INITIALIZE_ARGS.class,
Boolean.TYPE);
CK_C_INITIALIZE_ARGS ck_c_initialize_args = new CK_C_INITIALIZE_ARGS();
pkcs11 = (PKCS11) getInstanceMethod.invoke(null, libFile,
"C_GetFunctionList", ck_c_initialize_args, false);

java SunPKCS11 multiple etokens(smartcards) same time , provider not found error

I am using SSL connection with X509 certificates provided from smartcards.
I have 2 identical tokens from athena . I initialise the keystores after I am reading the certificates, but when I am trying to to do the actual connection for the second token I am getting no provider found for my Private key.Connecting using the first token it's not affected, it works.
I tried adding different SunPCKS11 provider by specifing the slotIndexList to 1 , the number for the second token given by "slots = p11.C_GetSlotList(true)", but still the same error.
When I am listing the providers: I see the second provider, but java doesn't use it (I don't know why).
Provider _etpkcs11;
slots = p11.C_GetSlotList(true);
if(slot ==0)
{
String pkcs11config = "name=Athena\nlibrary=C:\WINDOWS\system32\asepkcs.dll";
byte[] pkcs11configBytes =pkcs11config.getBytes();
ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11configBytes);
etpkcs11 = new SunPKCS11(configStream);
Security.addProvider(etpkcs11);
}
the above works
the following doesn't work
if(slot ==1)
{
String pkcs11config1 = "name=Athenaslot1\nlibrary=C:\WINDOWS\system32\asepkcs.dll";
byte[] pkcs11configBytes1 =pkcs11config1.getBytes();
ByteArrayInputStream configStream1 = new ByteArrayInputStream(pkcs11configBytes1);
etpkcs11 = new SunPKCS11(configStream1);
Security.addProvider(etpkcs11);
}
the following
for(int j=0;j<Security.getProviders().length;j++)
{
System.out.println(Security.getProviders()[j].getName());
}
returns:
SunPKCS11-Athena
SunPKCS11-Athenaslot1
SUN
SunRsaSign
SunEC
SunJSSE
SunJCE
SunJGSS
SunSASL
XMLDSig
SunPCSC
and the error when using the second the second token:
No installed provider supports this key: sun.security.pkcs11.P11Key$P11PrivateKey
Thanks
PS: I need the both tokens on same machine
After having a look at these docs it is saying that the instantiation of the SunPKCS11 can take a slot in the configuration.
So maybe you could try
String pkcs11config1 = "name=Athenaslot1\nslot=1\nlibrary=C:\WINDOWS\system32\asepkcs.dll";
Even though you add 2 providers to the list of providers, the SunPKCS11 class caches the first instance. It seems like it always uses this instance all the time. That's the reason your second provider is not picked up/identified.
You might have to write some sneaky code to approach your use case. Right before you use your second provider, you have to clear the cached instance. You can refer to this post here. It is unanswered, but the code you should be looking for is
Field moduleMapField = PKCS11.class.getDeclaredField("moduleMap");
moduleMapField.setAccessible(true);
Map<?, ?> moduleMap = (Map<?, ?>) moduleMapField.get(<YOUR_FIRST_PROVIDER_INSTANCE>);
moduleMap.clear(); // force re-execution of C_Initialize next time
What this basically does is clearing the cached instance. And now you can proceed to add your second provider instance to interact with your second token.

Categories