| Message |
|
|
Hi Igor,
I'm happy to hear that it works for you.
Regarding your questions:
- i have studied the crypted file produced and it is really closed to the ones i create with my applet using BouncyCastle so i would know if you have used BouncyCastle to encapsulate in PKCS7 ?
No, I can guarantee that all code in JCAPI is solely implemented by Pheox with one exception only; the CertFactory plugin is based upon parts of Bouncy Castle. In this case we have been very clear to state this in our documentation and we have also included Bouncy Castle's license file in the plugin JAR file.
The PKCS#7 functionality used in JCAPI is based upon the native PKCS#7 enabled streaming functions provided by MS CAPI and its installed CSPs. This is the reason why the performance is extremely good. I have not done any measurements, but I doubt that Bouncy Castle would reach JCAPI's figures since it's plain byte code processing with help from Hot Spot.
- when i crypt for two recipients (one with private key on a USB token and the other with private key in Windows store), whatever the order of the certificates it always ask me for the PIN code, so do you uncrypt for all the recipients ?
Ah, very interesting. I've not tried it myself, but I believe you. Just to be sure, I did double check my implementation and it's correct i.e. during encoding JCAPI forwards all certificates to MS CAPI in the same order as JCAPI receives them, and during decoding JCAPI iterates the certificates from MS CAPI as given by MS CAPI. This means that MS CAPI (well, the current CSP) do some kind of sorting of the certificates when they are to be stored in the envelope.
JCAPI will iterate through the certificates stored in the envelope and use the first one available whose certificate can be found in the MS CAPI MY system store with an associated private key. When a match is found, the iteration is stopped.
- your new version works very well and is compliant with some tokens which didn't worked with the previous one so do you think that it could be possible to make this new version the default one and become JCE compliant ? (your static methods encryptMessage and decryptMessage could be used to implements wrap and unwrap of the Cipher abstract class)
The new PKCS#7 functionality will be released in JCAPI 1.2.5. But it will remain as it is. The reason is that this is pure PKCS#7 functionality including both symmetric- and asymmetric cryptography plus ASN.1/BER encoding/decoding. The Cipher class is only used for plain symmetric- and asymmetric cryptography operations and it doesn't include the logic of PKCS#7 structures. Unfortunately, SUN has not defined any PKCS#7 APIs in Java, so we're stucked with divergent APIs.
- there's no mean to choose the private key to use for decryption when there are several ones possible so when the PIN code is asked you don't know which one you have to enter (it's dangerous when 5 Pin codes are possible (ie a file crypted with 5 certificates) thinking that most of the cards block after 3 tries.
This behavior is unfortunately not controlled by JCAPI. MS CAPI will display the PIN code dialog when access to a protected private key is required. This problem exists for all other applications as well.
JCAPI supports "certificate aware" PIN code dialogs, but then you have to use PKCS#11, which is out of the question in this case.
To conclude i wanted to tell you that your test program has a little bug : I've launched it as an applet and when the processing is finished but the applet still loaded i can't delete it (in Windows explorer : i've commented your deleteFiles method). This is corrected by closing the FileOutputStream after the BufferedOutputStream, for example :
Sounds like a bug in Java. If you take a look in the Java documentation for method close() in class BufferedInputStream it clearly says:
Closes this input stream and releases any system resources associated with the stream. Once the stream has been closed, further read(), available(), reset(), or skip() invocations will throw an IOException. Closing a previously closed stream has no effect.
This means that BufferedInputStream must call close() on aggregated streams as well i.e. on our FileInputStream instance.
And in your filesAreEqual method you don't close any FileInputStream nor BufferedInputStream.
Admit. That's a bug. Was too quick there. I'll do as Google and call this code BETA until no one complains anymore
I always make such bugs all the time. So don't worry about it.
I never worry
Ok, can we close this case now? It's not possible to get your token to work with CryptDecrypt, but we can circumvent it through the MS CAPI streaming API for PKCS#7.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Good, I'm looking forward to hear from you again after the test.
Btw, I did find a silly bug in my example test program. I've attached a corrected version now, please use that one instead.
Also, I made a performance test during encoding/decoding of a 1 Gb large file. I ran the test 3 times and took the average from it. The results are pretty good:
Encoding/encryption time: 103.48 seconds
Decoding/Decryption time: 140.94 seconds
For the test I used a 1024 bits RSA key and a 3DES key on a multi-core CPU (2.4 GHz), 4 Gb memory with a single disk.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Now I have finished the implementation. It was far more complex than I anticipated from the beginning, but now it works really good. I think you'll like it. As you'll see, it's very fast and neat to use.
You can download the upgraded version of JCAPI (v1.2.5 RC-2) from:
https://pheox.com/customer/download/personalfiles
I have also attached an example program to this post that shows how to use this new block based implementation.
Regarding your problem with your GemSafe card, you can see what CSP JCAPI is using by:
1. Find the alias name of the certificate that is stored on your card.
2. Use that alias as parameter when calling the JCAPI method JCAPIUtil.getCSP(alias)
I did check the web link you posted, but it doesn't mention any GemSafe Classic V2 TPC WR card. I would like to get my hands on the same card as you have, so can you please give me more information about how to get it?
Alright, can you please test the new JCAPI version together with your iKey token and see that everything is working for you? Please come back with your results and opinions.
Thanks.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Ok, no worries. If you find a retail channel for the card and the MS CAPI CSP, then please let me know the details about it.
Meanwhile I've found a block based solution that is very fast for large files. I've even succeeded to use my old Telia smart card (with SmartTrust CSP) together with it without the need of any PKCS#11 drivers. I think you'll like the solution. The problem is that I have to create a new Java class for it since there must be support for init, update, and final operations. It will take me a couple of days to fix it.
Stay tuned.
Regards,
Tommy
|
 |
|
|
Hi Igor,
I can get hold of an Omnikey CardMan 6121 dongle, but that product is just a card reader -> USB dongle i.e it doesn't support cryptographic functions itself, instead it's done by the SIM card to be inserted into that dongle.
Can you provide a SIM card and associated drivers (MS CAPI CSP etc)?
Regarding the memory problems using the new methods, yes, it should be used as a convenient solution for smaller messages. I'll check if there are any other better block based candidates available.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Have you had the possibility yet to test the new methods? We would like to have your response before we put the next release of JCAPI into system test.
Thanks.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Now I'm done with the implementation. I'm very satisfied with the result since the API got really easy to use, and the performance is very good.
Here are the new methods that I have added into class com.pheox.jcapi.JCAPICipher:
/**
* Encrypts the given message into a PKCS#7 enveloped data blob.
* The symmetric key algorithm used is Triple DES (3DES) with CBC
* block mode and PKCS#7 padding.
* The asymmetric key algorithm used is RSA with ECB block mode and
* PKCS#1 type 2 padding.
*
* @param recipients an array of X.509 certificates i.e one for each recipient.
* @param msg the message to be encrypted.
*
* @return the encrypted message.
*
* @throws GeneralSecurityException if <CODE>msg</CODE> cannot be encrypted. There might
* be many reasons for this e.g. the parameter <CODE>recipients</CODE> is null or
* any given certificate doesn't contain a RSA public key.
* @throws JCAPIJNIRuntimeException if an unexpected error occur inside MS CAPI.
*/
static public byte[] encryptMessage(X509Certificate[] recipients, byte[] msg)
throws GeneralSecurityException
/**
* Decrypts a PKCS#7 enveloped data blob.
* The recipient's certificate(s) must be stored in the MS CAPI system store MY.
*
* @param encryptedMsg the PKCS#7 encoded enveloped message to be decrypted.
*
* @return the decrypted message.
*
* @throws GeneralSecurityException if <CODE>encryptedMsg</CODE> cannot be encrypted.
* There might be many reasons for this e.g. the encrypted message is corrupt,
* or no private key can be found, or decryption algorithm is not supported
* by the MS CAPI CSP etc.
* @throws JCAPIJNIRuntimeException if an unexpected error occur inside MS CAPI.
*/
static public byte[] decryptMessage(byte[] encryptedMsg)
throws GeneralSecurityException
Here is an example program
import java.security.*;
import java.security.cert.X509Certificate;
import com.pheox.jcapi.*;
public class Example
{
public static void main(String[] args)
{
try {
Security.addProvider(new JCAPIProvider());
KeyStore ks = KeyStore.getInstance("msks", "JCAPI");
ks.load(null, null);
String alias = null;
//Get the alias of first available key entry.
for(java.util.Enumeration e = ks.aliases(); e.hasMoreElements(); )
{
alias = (String)e.nextElement();
if(ks.isKeyEntry(alias))
break;
else
alias = null;
}
if(alias == null)
throw new Exception("No key entry alias found.");
//Only use one recipient.
X509Certificate[] recipients = new X509Certificate[1];
recipients[0] = (X509Certificate)ks.getCertificate(alias);
//Encrypt message.
String msg = "This is a secret message.";
byte[] encryptedData = JCAPICipher.encryptMessage(recipients, msg.getBytes("UTF-8"));
//Decrypt message.
byte[] decryptedData = JCAPICipher.decryptMessage(encryptedData);
//Sanity check.
if(!msg.equals(new String(decryptedData, "UTF-8")))
throw new Exception("Decrypted message is not the same as the original message.");
else
System.out.println("Test succeded!");
} catch(Throwable t) {
System.err.println("Example program failed.");
t.printStackTrace();
}
}
}
Please verify that it works for your iKey as well.
Since you are a customer, you can download the new unrestricted version from:
https://pheox.com/customer/download/personalfiles
Regards,
Tommy
|
 |
|
|
Hi Igor,
Not being JCE compliant means that your code would have to call a method in JCAPI which is not part of the Java Cryptography Extension (JCE) API since JCE's Cipher class don't define a method which takes the data required for CryptDecryptMessage. It means that you can't just replace JCAPI with another JCE provider without changing and recompiling your source code.
Regarding your problems encountered with CryptDecryptMessage, I'm sure most of them can be circumvented. I guess the major problem is that we have to count for a performance penalty though, but I don't know how big it will be.
I agree that the simplest solution would be for your customers to just upgrade their drivers or change to another hardware key model, but it would be nice to support their keys too.
I'll do it like this; I'll make a test implementation to support CryptDecryptMessage in JCAPI to see if it solves your problem, and if it can be used in terms of performance and ease of use.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Thank you for the files.
I've installed your v3.4.1 driver on an XP system and took a chance it might work with my iKey 2032 device, but unfortunately the certificates don't show up at all. In this case, it seems impossible to get the problem reproduced without an iKey 2000 device.
I've searched the internet and discussed this matter with people who has more insight in this than me, and the message is clear; it's not possible to use/patch CryptDecryptMessage to mimic CryptDecrypt. Looks like a dead end.
However, I think the big problem here is that I've looked at it from a technician's perspective only, trying to get CryptDecryptMessage to work as CryptDecrypt in order to keep JCAPI strictly compliant with the JCE framework and solve your problem at the same time. The real problem here is actually that you want to encrypt/decrypt a session key still supporting the old CSP drivers for hardware tokens.
Can I propose the following solution instead? We can implement a new JCAPI method which will decrypt the enveloped data and thus use the CryptDecryptMessage function which works with your hardware token. This method won't of course be JCE compliant, but it will solve your problem.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Ok, no worries. Thanks for testing it.
It's quite problematic since I can't reproduce this on my iKey 2032 USB token. I'll see if I can get hold of an iKey 2000 token, but it seems hard to find one when searching the internet.
A couple of questions:
1. What version of the SafeNet software driver (iKey 2000) are you using? Can you please send it (zipped or installer) to us?
2. Do you encounter the same problem for any key entry (certificate & private key) on your iKey token? If yes, can you please do the following:
2.1 Create a self signed key entry and store it in a PKCS#12 key store.
2.2 Import this key store into your iKey token and validate that the same problem still occur.
2.3 Send this key store to us.
3. Can you please export all your current certificates from your iKey token and send it to us?
If you don't want to put the above files on this public forum, then you are welcome to send them to support@pheox.com.
Thanks.
Regards,
Tommy
|
 |
|
|
Hi Igor,
I've not yet found a solution for the CryptDecrypt vs CryptDecryptMessage problem.
However, I've found another way of retrieving a valid private key handle. Since I cannot reproduce your problem, could you please test the attached JCAPI.jar file to see if your test still fail? If we're lucky, it might work.
Please note that the JNI decryption function in this JCAPI.dll is heavily modified so you can only use it for your specific test case i.e. by providing a valid alias and not adding a PKCS#11 provider for your SafeNet CSP when running your test case.
Regards,
Tommy
|
 |
|
|
Hi Igor,
I can see that you are using CryptDecryptMessage() which acts a bit different from CryptDecrypt() which is used in JCAPI. A problem here is that CryptDecryptMessage() will do a lot more than we ask for i.e. it will envelope the data using a session key, and that's not what we want. I don't know if it's possible to mimic the behavior of CryptDecrypt() through CryptDecryptMessage().
Give me a couple of days and I'll see if I can come up with a solution. Would be nice to solve this issue.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Hmm, ok. Too bad we can't get it too work without the PKCS#11 patch solution.
I've tried to reproduce your problem on our iKey 2032 USB device (unfortunately we don't have a ikey 2000 device) with drivers v4.0.0.17, v3.4.9.4, and v3.4.7 on WinXP, but my encryption/decryption test never fails. It's very hard to fix it if we can't reproduce it.
You mentioned in an earlier post that you have had it worked in your own C++ implementation. One way to fix it might be to test your code in JCAPI. Is it possible for us to get a snippet of your test code to see how you have managed the decryption part?
Regards,
Tommy
|
 |
|
|
Hi,
Have you been able yet to check if the certificate has a private key or not?
Since no CRYPT_KEY_PROV_INFO struct is returned from the CSP, it shows a strong indication that there is no private key associated with that private key.
I've tested the getCSP() method with a certificate entry (i.e. no private key) and it trigger the exact same error as yours.
In any case, I'll change the implementation of the getCSP() method to also return the preferred CSP for certificate entries as well.
Regards,
Tommy
|
 |
|
|
Hi Igor,
Glad to hear that it worked for you.
Their MS CAPI CSP can be used for accessing the private, but for some reason it has a problem with the CAPI function that is used by JCAPI for decryption.
Just for curiosity, can you please run the same test again but using the new jcapi.jar file attached to this message (remember to remove the JCAPIUtil.addPKCS11CSP(...) method call from your source code)? I've created a new way of getting the private key handle and would like to see if it solves your problem.
Thanks.
Regards,
Tommy
|
 |
|
|
|
|