I'm try to develop a Java Application that is able to do the digital signature on a file with smart card.
I set the PKCS#11 provider in this way:
Security.addProvider(new IAIK());
Properties providerProperties = new Properties();
providerProperties.put("PKCS11_NATIVE_MODULE","path\\asepkcs.dll");
IAIKPkcs11 pkcs11Provider = new IAIKPkcs11(providerProperties);
Security.addProvider(pkcs11Provider);
Module module = Module.getInstance("path\\asepkcs.dll");
After I read the KeyStore that I load in to smart card before, but when I try to use the method for create the digital sign the application catch the follow exception:
java.security.SignatureException: iaik.pkcs.pkcs11.wrapper.PKCS11Exception: CKR_DEVICE_ERROR
at iaik.pkcs.pkcs11.provider.signatures.ExternalHashSignature.pkcs11Sign(ExternalHashSignature.java:294)
at iaik.pkcs.pkcs11.provider.signatures.PKCS11Signature.engineSign(PKCS11Signature.java:638)
at java.security.Signature$Delegate.engineSign(Unknown Source)
at java.security.Signature.sign(Unknown Source)
at sii.tesi.firma.provasc.FirmaScK.main(FirmaScK.java:288)
I'm not be able to understand how I could resolve the CKR_DEVICE_ERROR.
I use for the sign the follow methods:
Signature signAlg = Signature.getInstance("SHA1withRSA");
signAlg.initSign(privateKey);
signAlg.update(toBeEncrypted);
byte[] signatureValue = signAlg.sign();
I fixed CKR_DEVICE_ERROR. The problem was simply that the smartcard was making a bad connection (Omnikey 6121 are badly build)
a paperclip to jam the chip tighter on the board fixed this problem for me.
DId you use the Pkcs11Wrapper as well? If so, did you specify the java.library.path? The Wrapper version should match the Provider version, or check the readme for more details.
Regarding your code, specify the IAIK provider:
Signature.getInstance("SHA1withRSA", iaikProvider)
and try to add the provider like this:
IAIK.addAsProvider(false);
If that doesn't help check the error log generated by the pkcs11 module.
Related
My goal: I am working on an integration of Jetty Embedded that would make it simple to use. The interface would allow, among others, for integration of external sources for TLS certificates, without the use of the Java KeyStore.
This would allow for greater flexibility when building distributed web services (in my case an experimental, self-hosted CDN).
However, I am having problems building the integration. The stub implementation is in this repository.
What I've tried: I have tried replacing the key manager and the trust manager and set break points to every function in it. However, when trying to access the server, these break points are never triggered. Instead, I'm encountering this error:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1478)
at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:535)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:813)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:621)
at org.eclipse.jetty.server.HttpConnection.fillRequestBuffer(HttpConnection.java:322)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:231)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:112)
at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:261)
at org.eclipse.jetty.io.ssl.SslConnection$3.succeeded(SslConnection.java:150)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:112)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)
at java.lang.Thread.run(Thread.java:748)
I have tried analyzing the "standard" Jetty setup which a certificate from a keystore, but without much luck. I'm failing to find the point where Jetty is obtaining the cipher / certificate information that I should override.
My question: How can I get Jetty to use my own certificate source instead of the Java KeyStore and TrustStore?
#EJP pointed me in the right direction, so here's how to do it:
Here's how it needs to be done.
First, set up Jetty for TLS:
HttpConfiguration https = new HttpConfiguration();
https.addCustomizer(new SecureRequestCustomizer());
SslContextFactory sslContextFactory = new JettySslContextFactory(configuration.getSslProviders());
ServerConnector sslConnector = new ServerConnector(
server,
new SslConnectionFactory(sslContextFactory, "http/1.1"),
new HttpConnectionFactory(https)
);
sslConnector.setPort(httpsPort);
Note the class JettySslContextFactory. This class extends the built-in X509ExtendedKeyManager and needs to override the protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception method in order to provider a custom KeyManager, like this:
#Override
protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception {
return new KeyManager[] {
new JettyX509ExtendedKeyManager(certificateProviders)
};
}
In addition to that, the following steps are run in every connection:
The SNI matcher is consulted with the SNI host name. This seems to be the only place where the SNI host name is even available.
The key manager is consulted to get the alias (sort of a key ID) for a certain key type (EC or RSA). Here we need grab the host name from the SNI matcher, because otherwise we wouldn't know which host name to match on.
Based on the alias (key ID) we can then return the private key and certificate.
At least this is what I gathered from debugging this issue. The full code is online here.
I have a web application running on Tomcat. My application uses a web service which signs (via smartcard) and sends email. The web service itself adds the sunpkcs#11 provider automatically during the first call and before sending email, then can sign in and send emails if smartcard is not removed and inserted. If removed and inserted, in order to send email I must restart the tomcat server or it will give several errors depending on my code:
result= api.signAndSend(to, cc, bcc, subject, content, smartCardPin);
After removing and inserting smart card this code gives below exception message:
Token has been removed
These are my tryings:
I tried removing the sunpkcs#11 provider just after sending email and creating a new sunpkcs#11 provider and adding it.it gives and error like:
java.security.InvalidKeyException: No installed provider supports this
key: sun.security.pkcs11.P11Key$P11PrivateKey or
java.security.InvalidKeyException: No installed provider supports this
key: null
I did not remove sunpkcs#11 provider after each api.signAndSend(...) call,
rather :
result= api.signAndSend(to, cc, bcc, subject, content, smartCardPin);
SunPKCS11 sunPKCS11=(SunPKCS11)getLastProvider();
sunPKCS11.logout();
sunPKCS11.setCallbackHandler(new MyCallbackHandler());
KeyStore.CallbackHandlerProtection cpprotection = new KeyStore.CallbackHandlerProtection(
new MyCallbackHandler());
KeyStore.Builder builder = KeyStore.Builder.newInstance(
"PKCS11", sunPKCS11, cpprotection);
KeyStore ks = builder.getKeyStore();
//finalize PKCS#11
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(i expect this code to load pkcs#11 again but i am not sure)
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 = (PKCS11) getInstanceMethod.invoke(null, pkcs11Path,
"C_GetFunctionList", ck_c_initialize_args, false);
this code gives:
java.security.ProviderException: Initialization failed
at sun.security.pkcs11.P11Signature.initialize(P11Signature.java:319)
at sun.security.pkcs11.P11Signature.engineInitSign(P11Signature.java:432)
at java.security.Signature$Delegate.init(Signature.java:1127)
at java.security.Signature$Delegate.chooseProvider(Signature.java:1087)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1151)
at java.security.Signature.initSign(Signature.java:512)
at org.esign.bouncycastle.operator.jcajce.JcaContentSignerBuilder.build(Unknown Source)
.
.
.
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_KEY_HANDLE_INVALID
at sun.security.pkcs11.wrapper.PKCS11.C_SignInit(Native Method)
at sun.security.pkcs11.wrapper.PKCS11$SynchronizedPKCS11.C_SignInit(PKCS11.java:1721)
at sun.security.pkcs11.P11Signature.initialize(P11Signature.java:311)
java: 1.8.0.31
edit:
i remove and add SunPkcs#11 like this:
//the code below adds sunpkcss provider automatically after first call
result= api.signAndSend(to, cc, bcc, subject, content, smartCardPin);
//after each signAndSend i remove sunpkcs and add a new one
String sunpkcs11Name=getLastProvider().getName();
Security.removeProvider(sunpkcs11Name);
String cfg = MessageFormat.format(
"name = Starcos-SunPkcs11 library = c:/windows/system32/aetpkss1.dll slot = 52481 ");
InputStream is=new ByteArrayInputStream(cfg.getBytes());
SunPKCS11 newSunPkcs11Provider = new SunPKCS11(is);
Security.addProvider(newSunPkcs11Provider);
after i add a new SunPkcs11, while api.signAndSend(...) it gives:
java.security.InvalidKeyException: No installed provider supports this key: >sun.security.pkcs11.P11Key$P11PrivateKey
This exception is not because of absence of SunPkcs11 because i see the SunPkcs11 that i added, in the providers list.
It is hard to find exact solution in this kind of problem because it is hard to reproduce it so according to my reading PKCS#11 is already cover this Smartcards being inserted and removed scenario according to its documentation,
This is fine for an application that treats PKCS#11 tokens as static
keystores. For an application that wants to accommodate PKCS#11 tokens
more dynamically, such as Smartcards being inserted and removed, you
can use the new KeyStore.Builder class. Here is an example of how to
initialize the builder for a PKCS#11 keystore with a callback handler.
You already mention that remove and add provider is not working for you but according to this post they solve it via this way.
As per my knowledge both are same but one is working on one PC while same code says:
javax.crypto.NoSuchPaddingException: OAEPWITHSHA-256ANDMGF1PADDING unavailable with RSA on another machine.
When I am removing dash - from the name (OAEPWITHSHA256ANDMGF1PADDING) it starts running on another machine but leads error to some other line bad padding exception.
What could be the reason?
Sample code for Hint
I am using jdk1.7.0_71 32bit:
private byte[] decryptSecretKeyData(byte[] encryptedSecretKey, byte[] iv, PrivateKey privateKey) throws Exception
{
try {
Provider provider= new sun.security.pkcs11.SunPKCS11(keyStoreFile1);
Security.addProvider(provider);
LOG.info("**************Inside decryptSecretKeyData***********************");
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING", provider);
// decrypting the session key with rsa no padding.
rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
/* The reason is RSA OAEP SHA256 is not supported in HSM. */
byte[] decKey = rsaCipher.doFinal(encryptedSecretKey);
OAEPEncoding encode = new OAEPEncoding(new RSAEngine(), new SHA256Digest(), iv);
LOG.info("******************RSAPublicKey rsaPublickey = (*****************************");
java.security.interfaces.RSAPublicKey rsaPublickey = (java.security.interfaces.RSAPublicKey) publicKeyFile;
RSAKeyParameters keyParams = new RSAKeyParameters(false, rsaPublickey.getModulus(), EXPONENT);
encode.init(false, keyParams);
LOG.info("******************encode.processBlock(decKey, 0, decKey.length);************************");
byte decryptedSecKey[] = encode.processBlock(decKey, 0, decKey.length);
return decryptedSecKey;
} catch (InvalidCipherTextException e) {
LOG.info("*******************Failed to decrypt AES secret key using RSA :**********************");
throw new Exception("Failed to decrypt AES secret key using RSA :" + e.toString());
}
}
RSA/ECB/OAEPWITHSHA256ANDMGF1PADDING and RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING are different alias but both refers to the same algorithms, so in this sense there is not difference at all.
The thing is that you're using a PKCS#11 (cryptographic token interface) to cipher your data. According to java PKCS#11 reference:
The Sun PKCS#11 provider, in contrast to most other providers, does not implement cryptographic algorithms itself.
Instead, it acts as a bridge between the Java JCA and JCE APIs and the
native PKCS#11 cryptographic API, translating the calls and
conventions between the two. This means that Java applications calling
standard JCA and JCE APIs can, without modification, take advantage of
algorithms offered by the underlying PKCS#11 implementations, such as,
for example,
Cryptographic Smartcards, Hardware cryptographic accelerators, and
High performance software implementations. Note that Java SE only
facilitates accessing native PKCS#11 implementations, it does not
itself include a native PKCS#11 implementation. However, cryptographic
devices such as Smartcards and hardware accelerators often come with
software that includes a PKCS#11 implementation, which you need to
install and configure according to manufacturer's instructions.
Summarized, if you're using a PKCS#11, the use of the algorithms depends on vendors native implementation (.dll on windows, .so on linux ...) and some times on specific program connector so: check if in the both PCs you're using the same drivers/program version for your PKCS#11 token and the both are correctly installed because probably there is an error in one of them which doesn't allows you to use RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING correctly.
Hope this helps,
Both algorithms are provided by the different security provider. The
RSA/ECB/OAEPWITHSHA256ANDMGF1PADDING
is provided by the Bouncy Castle provider while
RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING
is provided by the SUN JCE provider. In our case, we are able to successfully use Bouncy Castle provider algorithm but if I replace that with SUN JCE algorithm then it gives following error:
Exception in thread "main" javax.crypto.BadPaddingException: lHash mismatch
at sun.security.rsa.RSAPadding.unpadOAEP(RSAPadding.java:425)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:274)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:382)
The situation is more complex.
The RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING is nominally provided by SunJCE.
The RSA/NONE/OAEPWITHSHA256ANDMGF1PADDING is provided by BC.
However BC is able to take over and act as a provider of RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING if it is installed before the SunJCE provider (using Security.installProviderAt). Unfortunately, BC does not parametrize this algorithm the same way as SunJCE does it. BC implementation of that algorithm is thus different (!) than one provided by SunJCE, which will be source of padding errors.
SunJCE RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING is using SHA-1 for MGF1 function and SHA-256 for label hash (which is almost always an empty byte array and therefore a static value).
BC RSA/NONE/OAEPWITHSHA256ANDMGF1PADDING is using SHA-256 for MGF1 function and SHA-256 for label hash. BC will also use the same parameters if it is being asked to provide RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING.
Advice: always explicitly specify security provider to make sure that correct provider is used. Alternatively explicitly specify the OAEP parameters and don't rely on defaults (recommended).
I've reported the issue to BC, so maybe it will get fixed.
I'm developing (java) a xml verification application which needs to connect to a windows keystore.
At the moment I'm stuck at the following message: CannotBuildCertificationPathExecption: Trust anchors Keystore is not initialized.
Now I was able to get my key from the store using this example:http://stackoverflow.com/questions/5476974/java-access-to-intermediate-cas-from-windows-keystores Which works great. And gave me hope in using XAdES4J.
The code I'm using is the following:
trustAnchors = KeyStore.getInstance("Windows-MY");
certValidator = new PKIXCertificateValidationProvider(trustAnchors, false);
p = new XadesVerificationProfile(certValidator);
v = p.newVerifier();
Element sigElem = (Element) signature.item(0); //Which contains the complete signature segment from the xml
XAdESVerificationResult r;
SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().useBaseUri("http://www.ietf.org/rfc/");
r = v.verify(sigElem, options);
The certificate is a x509. The encryption method XAdES-t.
Does anybody know how to get a trusted connection with a windows keystore?
Is there any information about SignatureSpecificVerificationOptions. I find it really hard to understand the manual in context with the actual settings I need to use..
Even though it is a Wndows keystore you still need to load it:
trustAnchors.load(null);
The PKIXCertificateValidationProvider can't do it because protection parameters may be required.
In addition, you may want to use "Windows-ROOT" instead of "Windows-MY" in order to access the trusted certification authorities.
I've managed to use Sun's MSCAPI provider in my application. The problem I'm having now is that it always pops up a window, asking for a password, even though I've provided it in the code. This is a problem, because I need the cryptography functionality in a webservice.
Here's the code I have now:
String alias = "Alias to my PK";
char[] pass = "MyPassword".toCharArray();
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, pass);
Provider p = ks.getProvider();
Signature sig = Signature.getInstance("SHA1withRSA",p);
PrivateKey key = (PrivateKey) ks.getKey(alias, pass)
sig.initSign(key);
sig.update("Testing".getBytes());
sig.sign();
This is working great, but I get a popup asking for the password when the last line is run. How do I prevent that?
The MSCAPI provider does not support providing the password to CAPI:
A compatibility mode is supported for applications that assume a password must be supplied. It permits (but ignores) a non-null password. The mode is enabled by default. (1)
To set the password through CAPI, you must call CryptSetKeyParam with the undocumented KP_KEYEXCHANGE_PIN or KP_SIGNATURE_PIN and hope your underlying hardware token provider supports it. (They are not completely undocumented - the documentation for Windows CE and Windows Mobile mention them (2) and they are included in the header files).
My guess is that Windows is popping up the pop up.
Import your key again using the Certificate Import Wizard, but make sure that you don't check the following option on the "Password" screen.
[_] Enable strong private key protection. You will be prompted every time the private key is used by an application if you enable this option.
I resolved this problem setting the provider as follow:
signeData = gen.generate(content, ks.getProvider());
Where
ks is a KeyStore and
genis a CMSSignedDataGenerator