remove and insert smartcard using sunpkcs#11 and tomcat - java

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.

Related

etoken unaccessible after reading

I´m trying to read out the certificate of an etoken. I´ve followed the answer from Keystore from digital signature e-token using java. It´s giving me the certificates installed in the token but after that the token isn´t reachable anymore. Did somebody got something similar while accessing a token?
// Create instance of SunPKCS11 provider
String pkcs11Config = "name=eToken\nlibrary=C:\\path\\to\\your\\pkcs11.dll";
java.io.ByteArrayInputStream pkcs11ConfigStream = new java.io.ByteArrayInputStream(pkcs11Config.getBytes());
sun.security.pkcs11.SunPKCS11 providerPKCS11 = new sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream);
java.security.Security.addProvider(providerPKCS11); // Get provider KeyStore and login with PIN String pin = "11111111";
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS11", providerPKCS11);
keyStore.load(null, pin.toCharArray()); // Enumerate items (certificates and private keys) in the KeyStore
java.util.Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
System.out.println(alias);
}
The problem persists, after plugging out/in the token is reachable again, but after running the code, the token seems to be locked again. OS Win2k8 Server.
Finally got this clear. After disconnecting other USB devices the Token responds as usual.
The Token should be plugged in to a fully powered port. Best on a separate Host- Bus.

java.lang.NullPointerException Error during the SecurityHelper.prepareSignatureParams() OpenSAML call [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I have hit a dead end using the OpenSAML support for preparing a SAML Payload to accomplish a SSO transaction with another service that I am working with. I receive a NullPointerException that is thrown after I use the SecurityHelper.prepareSignatureParams() routine. I have a Stacktrace, but it wold be pretty ugly to append.
Let me first say what I was able to do...
For the purposes of learning the technology and to make sure it would work, I was able to successfully build a SAML payload, sign it using a Certificate and a Private Key that was stored in a Java Key Store file that I created locally on my workstation using the Keytool program with the -genkeypair option.
As I understand things, my JKS file contains a Self Signed Certificate and a Private Key. I was able to open the JKS file, gather the Certificate and the Private Key to build a Signing Certificate. The Signing Certificate was used to sign the SAML Payload that I created You'll see how I did this if you look at the code samples that I'll append.
What isn't working...
I want to use the same SAML support to sign my SAML Payload using a Trusted Certificate that I have for my website that I received from GoDaddy. To do this, I installed the Trusted Certificate into my webserver's keystore at: '\Program Files\Java\jre1.8.0_102\lib\security\cacerts'. I understand that the cacerts file is the KeyStore for our webserver. I installed the Trusted Certificate using the Keytool -importcert command. One big difference is that the Trusted Certificate DOESN'T have a Private Key. So when preparing the Signing Certificate using the Open SAML support, I am not able to add a Private Key to the Credential object (because I don't have one).
When attempting the above for the Trusted Certificate, I am able to get to the part where I am preparing the Signature Parms (SecurityHelper.prepareSignatureParams()). That's where I get the Null Pointer.
If you could take a look at the code that I am using. I am including the code (that signs my payload successfully) that reads from the local JKS file and also the code (that gets the Null Pointer Exception) when I try to using the Trusted Certificate on the server (both cases). There's not much different between the two cases:
// Signing process using OpenSAML
// Get instance of an OpenSAML 'KeyStore' object...
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// Read KeyStore as File Input Stream. This is either the local JKS
// file or the server's cacerts file.
File ksFile = new File(keyStoreFileName);
// Open an Input Stream with the Key Store File
FileInputStream ksfInputStream = new FileInputStream(ksFile);
// Load KeyStore. The keyStorePassord is the password assigned to the keystore, Usually 'changeit'
// before being changed.
keyStore.load(ksfInputStream, keyStorePassword);
// Close InputFileStream. No longer needed.
ksfInputStream.close();
// Used to get Entry objects from the Key Store
KeyStore.PrivateKeyEntry pkEntry = null;
KeyStore.TrustedCertificateEntry tcEntry = null;
PrivateKey pk = null;
X509Certificate x509Certificate = null;
BasicX509Credential credential = null;
// The Java Key Store specific code...
// Get Key Entry From the Key Store. CertificateAliasName identifies the
// Entry in the KeyStore. KeyPassword is assigned to the Private Key.
pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(certificateAliasName, new KeyStore.PasswordProtection(keyPassword));
// Get the Private Key from the Entry
pk = pkEntry.getPrivateKey();
// Get the Certificate from the Entry
x509Certificate = (X509Certificate) pkEntry.getCertificate();
// Create the Credential. Assign the X509Certificate and the Privatekey
credential = new BasicX509Credential();
credential.setEntityCertificate(x509Certificate);
credential.setPrivateKey(pk);
// The Trusted Certificate specific code...
// Accessing a Certificate that was issued from a trusted source - like GoDaddy.com
//
// Get Certificate Entry From the Key Store. CertificateAliasName identifies the Entry in the KeyStore.
// There is NO password as there is no Private Key associate with this Certificate
tcEntry = (TrustedCertificateEntry) keyStore.getEntry(certificateAliasName, null);
// Get the Certificate from the Entry
x509Certificate = (X509Certificate) tcEntry.getTrustedCertificate();
// Create the Credential. There is no Provate Ley to assign into the Credential
credential = new BasicX509Credential();
credential.setEntityCertificate(x509Certificate);
// Back to code that is not specific to either method...
//
// Assign the X509Credential object into a Credential Object. The BasicX509Credential object
// that has a Certificate and a Private Key OR just a Certificate added to it is now saved as a
// Cendential object.
Credential signingCredential = credential;
// Use the OpenSAML builder to create a signature object.
Signature signingSignature = (Signature) Configuration.getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME).build Object(Signature.DEFAULT_ELEMENT_NAME);
// Set the previously created signing credential
signingSignature.setSigningCredential(signingCredential);
// Get a Global Security Configuration object.
SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();
// KeyInfoGenerator. Not sure what this is, but the example I am working from shows
// this being passed as null.
String keyInfoGeneratorProfile = "XMLSignature";
// Prepare the Signature Parameters.
//
// This works fine for the JKS version of the KeyStore, but gets a Null Pointer exception when I run to the cacerts file.
SecurityHelper.prepareSignatureParams(signingSignature, signingCredential, secConfig, keyInfoGeneratorProfile <or null>);
// I need to set into the SigningSignature object the signing algorithm. This is required when using the TrustedCertificate
signingSignature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
// This is my code that builds a SAML Response. The SAML Payload contains data
// about the SSO session that I will be creating...
Response samlResponse = createSamlResponse.buildSamlResponseMessage();
// Sign the Response using the Certificate that was created earlier
samlResponse.setSignature(signingSignature);
// Get the marshaller factory to marshall the SamlResponse
MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory();
Marshaller responseMarshaller = marshallerFactory.getMarshaller(samlResponse);
// Marshall the Response
Element responseElement = responseElement = responseMarshaller.marshall(samlResponse);
// Sign the Object...
Signer.signObject(signingSignature);
NOTE: My attempt to sign a SAML Payload was modeled after an OPENSAML example that I found here: https://narendrakadali.wordpress.com/2011/06/05/sign-assertion-using-opensaml/
Hoping that someone can show me the error of my ways or what I am missing.
Thanks for any suggestions.
EDIT (01/26/2016)
I was able to get past the NULL pointer I was receiving while preparing the Signature Params (SecurityHelper.prepareSignatureParams()). Code changes included updating my xmlsec.jar file to version 2.0.8 (xmlsec-2.0.8.jar) and I explicitly setting the signature algorithm to SHA256 when using the Trusted Certificate (from GoDaddy). See my code example for the use of:
signingSignature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
The above changes allows the SAML payload to be built and sent to the connection endpoint.
However, I am still not establishing the SSO connection to my endpoint.
Here's what I see happening:
During processing while the SAML payload is being constructed and specifically, the SAML payload's Signature is being signed:
Signer.signObject(signature);
I get an an error message from SAML:
ERROR: org.opensaml.xml.signature.Signer - An error occured computing the digital signature
The stack trace (just the ending portion):
org.apache.xml.security.signature.XMLSignatureException: Sorry, you supplied the wrong key type for this operation! You supplied a null but a java.security.PrivateKey is needed.
at org.apache.xml.security.algorithms.implementations.SignatureBaseRSA.engineInitSign(SignatureBaseRSA.java:149)
at org.apache.xml.security.algorithms.implementations.SignatureBaseRSA.engineInitSign(SignatureBaseRSA.java:165)
at org.apache.xml.security.algorithms.SignatureAlgorithm.initSign(SignatureAlgorithm.java:238)
at org.apache.xml.security.signature.XMLSignature.sign(XMLSignature.java:631)
at org.opensaml.xml.signature.Signer.signObject(Signer.java:77)
I searched the error messages, but I am not coming up with much.
I don't understand the root of the error message - That the wrong key type was supplied (null) and that OpenSAML seems to be expecting a java.Security.PrivateKey.
When using the Trusted Certificate, I don't have a Private Key, Correct? How would I be able to provide a Private Key? In the case of the Trusted Certificate I read a Trusted Certificate (TrustedCertificateEntry) from the KeyStore. The TrustedCertificateEntry object allows me to access the Certificate, but there's no method for obtaining a Private Key (as well there shouldn't be).
However, when I use my Self Signed Certificate to perform the signing operation, I understand that I do have both the Certificate (the Public Key) and the Private Key contained in the JKS file (the KeyStore). I think that's why when I read from the JKS file, I am able to read a Private Key Entry (KeyStore.PrivateKeyEntry) that has methods for accessing both the Public Key (the Certificate) and the Private Key.
What am I missing about the Trusted Certificate case? The OpenSAML support seems to be expecting a Private key to be able to compute the Signature.
In the case of the Trusted Certificate, is there a way to package the original Private Key into my Key Store (along with the Trusted Certificate)? I am not sure if this is what is normally done or even possible.
Hopefully some guidance as to what I am doing here, Please!
EDIT (01/26/2017) - 2 to provide additional detail.
I'll share a portion of the SAML payload that gets sent...
In the case of the Self Signed Certificate, I see a SignatureValue tag and a X509Certificate tag. Both have binary data included within the begin and end of the tag.
In the case of the Trusted Certificate, I've got an empty Signature Value tag that looks like:
<ds:SignatureValue/>
The Certificate tag is still present and contains the certificate bytes.
So, looking at the error I see from OpenSAML, it is more obvious that it can't compute a Signature using the data that is available in the Trusted Certificate.
Ok, this quite a long question. As I understand the root of the problem is the message "Sorry, you supplied the wrong key type for this operation! You supplied a null but a java.security.PrivateKey is needed." You are trying to sign a message using a public key. This is not possible. Just looking logically on it, signing using a public key would not provide any proof that the signer is intended as it is available to everyone.
What you need to do is sign using a private key. in your case you have generated a public and private key on you computer, then sent CSR to the CA and recieved a certificate signed by the CA.
You should use the privat key from you local computer to sign the message and send the CA signed certificate to the recipient so they can use it to confirm your signature.
In this blog post of mine I explain how to obtain credentials, including the private key from a Java keystore.

CKR_DEVICE_ERROR using iaik for digital signature

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.

How, with XAdES4j, to trust anchors to a windows keystore?

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.

Java security - MSCAPI provider: How to use without password popup?

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

Categories

Resources