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

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.

Related

TLS cipher suite text to integer or hex value lookups in Java from some library?

I have some codebase that uses TLS and it works with the actual cipher processing via the integer value of the cipher selected. The selected cipher is extracted as follows:
String cipherSuite = sslSocket.getSession().getCipherSuite();
which is a value like TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, and has an integer value of 49200 or 0xc030 in hex. The codebase has a manually defined mapping class that returns the integer value of the cipher given the cipher name.
I took a look at the SSLSocket object chain there from an IDE to see what other methods were available, but didn't find anything that could do the lookup that the manual mapped class did.
From another faintly related SO post online, I found a table of TLS ciphers and their values: https://web.archive.org/web/20151219054439/http://www.thesprawl.org/research/tls-and-ssl-cipher-suites/.
Now I'm just wondering rather than go through the effort in maintaining the full table mapping, in case things change in future, is there already some Java class that we can call to do the lookup? e.g. lookup by cipher name to get integer/hex value or vice versa? I'm no security expert nor a Java guru, so wouldn't know.
P.S. the codebase I was working with didn't have the full mapping of the table link I mentioned, only a partial subset. Came across this problem as the codebase was failing from missing some ciphers that were now being used in the system. I added the missing ciphers, but looking for a more elegant way to maintain the cipher mapping or lookup.
You can get the standard TLS ciphersuite code for an existing session. You are vague how you "took a look ... from an IDE" but if you look with a debugger you should see that the SSLSessionImpl for the socket (or engine) contains cipherSuite which in turn contains both int id (the code) and String name (the name), although the API for SSLSession returns only the latter. For example in my Eclipse:
Since these classes and their fields (and methods) are not public you can't access them directly, but at least through j8 you can do so with reflection:
SSLSocket sock = (SSLSocket) SSLSocketFactory.getDefault().createSocket("example.com", 443);
SSLSession sess = sock.getSession(); // actually sun.security.ssl.SSLSessionImpl
Class<?> c1 = Class.forName("sun.security.ssl.SSLSessionImpl");
Field f1 = c1.getDeclaredField("cipherSuite"); f1.setAccessible(true);
Object cs = f1.get(sess);
Class<?> c2 = Class.forName("sun.security.ssl.CipherSuite");
Field f2 = c2.getDeclaredField("id"); f2.setAccessible(true);
Integer id = (Integer) f2.get(cs);
System.out.printf("%x%n", id);
In the 'module' scheme of j9 up this gives a warning for 'illegal reflective access', although so far (up to 13.0.1) it does actually succeed. If I get a better handle on exactly how modules work I will try to improve this. (Or this being Stack, someone else can.)
Note this doesn't actually provide a mapping as you asked, only the value for the current connection which appears to be your actual need. If you really want a mapping, CipherSuite is actually an enum class so all of the instances are in fact there, but looking at the code it appears the method used to find this mapping has been changed in different versions, as well as being nonpublic, so I'm not going to the trouble of trying to work out multiple examples without knowing which if any you need.

Java use SecureRandom with SunPKCS11 provider

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(.., ..).

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);

Password Reset Enforcing Directory Policies with UnboundID

I'm developing a web app that let users reset their own passwords in Active Directory. I've been doing it by binding as an administrator and it works fine, but the directory policies (reuse history, characters, etc) are not being enforced. I can't bind as a user because I don't have the current password.
I read about the LDAP_SERVER_POLICY_HINTS control introduced in Windows 2008 R2 SP1 for doing that in Active Directory and even found someone who made it using Spring LDAP
Since I'm using UnboundID and there is no standard control shipped for that, I figured that I had to create my own control class. The documented OID is 1.2.840.113556.1.4.2239 and the value {48, 3, 2, 1, 1}
public class PolicyHintsControl extends Control {
private static final long serialVersionUID = 1L;
public final static String LDAP_SERVER_POLICY_HINTS_OID = "1.2.840.113556.1.4.2066";
public final static byte[] LDAP_SERVER_POLICY_HINTS_DATA = { 48,
(byte) 132, 0, 0, 0, 3, 2, 1, 1 };
public PolicyHintsControl() {
super(LDAP_SERVER_POLICY_HINTS_OID, false, new ASN1OctetString(
LDAP_SERVER_POLICY_HINTS_DATA));
}
#Override
public String getControlName() {
return "LDAP Server Policy Hints Control";
}
#Override
public void toString(StringBuilder buffer) {
buffer.append("LDAPServerPolicyHints(isCritical=");
buffer.append(isCritical());
buffer.append(')');
}
}
So I added this new control in the modify request like this:
public static void main(String[] args) throws Exception {
final String host = "ldap.example.com";
final int port = 636;
String adminDn = "admin#example.com";
String adminPassword = "passwd";
String userDn = "CN=user,ou=people,dc=example,dc=com";
String userPassword = "passwd";
String keystoreFile = "/path/to/keystore.jks";
String keystorePassword = "passwd";
String passwordAttribute = "unicodePwd";
//Password change requires SSL
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(keystoreFile), keystorePassword.toCharArray());
TrustManagerFactory factory = TrustManagerFactory.getInstance("x509");
factory.init(keyStore);
final SSLUtil sslUtil = new SSLUtil(factory.getTrustManagers());
SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory();
Debug.setEnabled(true);
// Connect as the configured administrator
LDAPConnection ldapConnection = new LDAPConnection(socketFactory, host,
port, adminDn, adminPassword);
// Set password in AD format
final String newQuotedPassword = "\"" + userPassword + "\"";
final byte[] newPasswordBytes = newQuotedPassword.getBytes("UTF-16LE");
String encryptedNewPwd = new String(newPasswordBytes);
//Build modifications array and request
final ArrayList<Modification> modifications = new ArrayList<Modification>();
modifications.add(new Modification(ModificationType.REPLACE,
passwordAttribute, encryptedNewPwd));
ModifyRequest modifyRequest = new ModifyRequest(userDn, modifications);
//Add the policy hints control
modifyRequest.addControl(new PolicyHintsControl());
//Modify already
ldapConnection.modify(modifyRequest);
ldapConnection.close();
}
I get the following exception:
Exception in thread "main" LDAPException(resultCode=53 (unwilling to perform), errorMessage='0000052D: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
', diagnosticMessage='0000052D: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
')
After researching a bit more I found that there was another update in Windows 2012 for the same control which changed the OID to 1.2.840.113556.1.4.2066 and deprecated the old OID.
Since this app can be configured with any version of AD I'd like to handle gracefully every scenario (Windows 2012, Windows 2008 R2 SP1, others). My questions are:
Does anyone have successfully done this with UnboundID?
Is there anyway to know if the controls are available before the modification request?
What would be the best way to handle different OID's for different versions of AD for the same control? Same class or different classes?
I'm not all that familiar with Microsoft-specific controls so I can't provide a lot of help there, but it looks like you're already on the right track with that. In this case, it actually looks like the control is working as expected and the server is rejecting the password because it's not strong enough.
Active Directory is really awful with how hard it makes it to figure things like this out, but the secret lies in the "0000052D" given in the diagnostic message. That is a reference to Active Directory system error code 0x52D, which is decimal 1325. System error codes are documented at http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx, and in this case you need to follow the "System Error Codes (1300-1699)" link (http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx) and find the description for value 1325. The text for that error code says "Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain." Since the point of the control you're trying to use seems to be to cause the server to perform quality checking on the new password, it looks like it's working as expected. If you use a stronger password (e.g., make it longer, include uppercase/numeric/symbol characters, etc.) then perhaps the server will accept it.
With regard to your question about figuring out what controls the server supports, the way to do that is to retrieve the server root DSE and look at the OIDs reported in the supportedControls attribute. The UnboundID LDAP SDK for Java makes this pretty easy because you can use the LDAPConnection.getRootDSE method to retrieve the root DSE, and then the RootDSE.supportsControl method to determine whether the server supports the specified control.
With regard to your question about whether to handle different OIDs with the same class or different classes, that's more a matter of style than anything else. If the control with the newer OID also uses a different encoding for the value, then that would definitely suggest making a separate class. If the value encoding is the same for both OIDs, then it's probably a matter of personal preference but even if you make them separate classes then it would be good to keep the majority of the code common rather than having the same code in two different places.

Issue in Loading Internet Explorer Keystore in JAVA

I am building a cryptographic web application, which allows user to sign and encrypt on client side within browser. The application has to be compatible with IE and Firefox. It works perfectly fine in Firefox in IE I am facing one issue.
Recently, Government of India started issuing two digital certificates to individuals one for signing data and one for encryption. These certificates have same CN. I am using following code to loop through certificates.
public static Certificate selectedCert = null;
KeyStore keystore1 ;
keystore1 = KeyStore.getInstance("Windows-MY");
keystore1.load(null, null);
if (keystore1 != null) {
enumeration = keystore1.aliases();
while (enumeration.hasMoreElements()) {
alias = enumeration.nextElement();
selectedCert = keystore1.getCertificate(alias));
System.out.println(selectedCert.getPublicKey());
}
}
While reading certificate from firefox keystore the alias names are generated by firefox which are unique but in case of IE, it takes certificates from IE it takes Common Name (CN) as alias. That way it gets two such entries with same alias. Now whenever I want to get the whole certificate object I have to pass alias, so whenever I pass alias it will always give me first certificate and I am unable to access the second certificate with same alias name.
To clarify more, if I have two certificates in the name of "Kuntal Shah" and one in name of "Abhishek Desai".
Then the aliases enumeration will have
"Kuntal Shah"
"Kuntal Shah"
"Abhishek Desai"
when I do
selectedCert = keystore1.getCertificate(alias));
It always returns me the first one and I am never able to get the second one.
I tried some code in C#, there I have a simpler solution
X509Store storeMy = new X509Store(StoreName.My,StoreLocation.CurrentUser);
storeMy.Open(OpenFlags.ReadOnly);
Console.WriteLine("Found certs with the following subject " +"names in the {0} store:", storeMy.Name);
foreach (X509Certificate2 cert in storeMy.Certificates)
{
Console.WriteLine("\t{0}", cert.SubjectName.Name);
}
But this does not work with firefox and it will not work on Linux.
Can any one tell me how do I access the second certificate? or is there any other different way all together?
This solution is even uglier than my other one, but it may actually work. You can use reflection to get direct access to the collection of all the key and certificate entries.
public void printKeystore() {
Field spiField = KeyStore.class.getDeclaredField("keyStoreSpi");
spiField.setAccessible(true);
KeyStoreSpi spi = (KeyStoreSpi) spiField.get(keystore1);
Field entriesField = spi.getClass().getSuperclass().getDeclaredField("entries");
entriesField.setAccessible(true);
Collection entries = (Collection) entriesField.get(spi);
for (Object entry : entries) {
String alias = (String) invokeGetter(entry, "getAlias");
Key privateKey = (Key) invokeGetter(entry, "getPrivateKey");
X509Certificate[] certificateChain = (X509Certificate[]) invokeGetter(entry, "getCertificateChain");
System.out.println(alias + ": " + privateKey + Arrays.toString(certificateChain));
}
}
private Object invokeGetter(Object instance, String methodName)
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
Method getAlias = instance.getClass().getDeclaredMethod(methodName);
getAlias.setAccessible(true);
return getAlias.invoke(instance);
}
I don't like this solution, but maybe you can call deleteEntry. Load the alias you want and check if the cert is the one you want. If not, delete it. Then try to load it again. Maybe you can read them all that way. Just make sure not to call KeyStore.store. You don't want to delete the certificates in IE.

Categories