Issue in Loading Internet Explorer Keystore in JAVA - 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.

Related

SecretKeyFactory.getInstance gives me NoSuchAlgorithmException no matter what algorithm I provide

So I have been looking at this page: https://developer.android.com/reference/javax/crypto/SecretKeyFactory.html
They appear to claim that any of those algorithms should work with SecretKeyFactory in Android-Studio but when I put in this code:
SecretKeyFactory key = SecretKeyFactory.getInstance("Algorithm-Name");
No matter what algorithm name I put in that is listed on that android website, it always gives me
Unhandled exception: java.security.NoSuchAlgorithmException
I tried it in eclipse with a standard Java project and it worked fine.
I tried using spongycastle too but that didn't seem to work either.
There seems to be something wrong with your provider configuration. You could try and list which algorithms are available using the following code:
Provider[] providers = Security.getProviders();
for (Provider provider : providers) {
boolean printedProvider = false;
Set<Service> services = provider.getServices();
for (Service service : services) {
String algorithm = service.getAlgorithm();
String type = service.getType();
if (type.equalsIgnoreCase("SecretKeyFactory")) {
if (!printedProvider) {
System.out.printf("%n === %s ===%n%n", provider.getName());
printedProvider = true;
}
System.out.printf("Type: %s alg: %s%n", type, algorithm);
}
}
}
If this doesn't print anything then create an empty project for the same runtime, and run it again. If it does show an algorithm - and it should - then you should make sure that the printed provider is available in your project configuration.
If it doesn't, you should really reinstall the SDK or runtimes, as there is something seriously wrong with the project setup.

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

Add JFace Key Scheme Programmatically

I thought about repurposing org.eclipse.jface.bindings.Scheme to store key bindings on a per user base:
String userName = "Bob";
BindingManager bindingManager = ((BindingService) PlatformUI.getWorkbench().getService(IBindingService.class)).getBindingManager();
Scheme scheme = bindingManager.getScheme(userName);
scheme.define(userName, "Scheme for user " + userName, DEFAULT_SCHEME);
bindingManager.setActiveScheme(scheme);
Which works well for some moments, but whenever the schemes get loaded from the preferences (e.g. via CommandPersistence#reRead) only the schemes defined in the plugin.xml will be read and everything else gets discarded.
Especially this method of the class BindingService is a problem:
public final void savePreferences(final Scheme activeScheme,
final Binding[] bindings) throws IOException {
// store everything in preferences, then read everything
// -> custom schemes get removed
BindingPersistence.write(activeScheme, bindings);
// now the removed (undefined) scheme gets set
bindingManager.setActiveScheme(activeScheme);
bindingManager.setBindings(bindings);
}
Since I can't really register all users via plugin.xml, how can I register schemes programmatically?
As a "solution", I just re-implemented the scheme for our use case:
String userName = "Bob";
String keyBindings = MyPlugin.getDefault().getPreferenceStore().getString("keyBindings." + userName);
PlatformUI.getPreferenceStore().setValue(PlatformUI.PLUGIN_ID + ".commands", keyBindings);
This triggers CommandPersistence#reRead as well, but since I don't have my custom scheme this time, it doesn't fail. Now the management of our different schemes is our problem, but at least that way it works.

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.

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