/*
Websocket Smartcard Signer
Copyright (C) 2017 Damiano Falcioni (damiano.falcioni@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package df.sign.pkcs11.impl.jna;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import org.pkcs11.jacknji11.C;
import org.pkcs11.jacknji11.CE;
import org.pkcs11.jacknji11.CKA;
import org.pkcs11.jacknji11.CKM;
import org.pkcs11.jacknji11.CKO;
import org.pkcs11.jacknji11.CKU;
import org.pkcs11.jacknji11.CK_MECHANISM_INFO;
import org.pkcs11.jacknji11.CK_SESSION_INFO;
import org.pkcs11.jacknji11.CK_TOKEN_INFO;
import org.pkcs11.jacknji11.jna.JNA;
import df.sign.SignUtils;
import df.sign.pkcs11.CertificateData;
import df.sign.pkcs11.SmartCardAccessI;
import df.sign.utils.StringUtils;
import df.sign.utils.X509Utils;
public class SmartCardAccessJnaImpl implements SmartCardAccessI {
public long[] connectToLibrary(String library) throws Exception, Error{
System.out.println("Connection to " + library);
C.NATIVE = new JNA(library);
CE.Initialize();
long[] slotList = CE.GetSlotList(true);
if(slotList.length==0)
throw new Exception("Unable to find smart card using library " + library);
ArrayList retArrLst = new ArrayList();
for(long slot:slotList){
try{
long[] mechLst = CE.GetMechanismList(slot);
if(SignUtils.isContainedIntoArray(CKM.RSA_PKCS, mechLst)){
CK_MECHANISM_INFO myMechanismInfo = (CK_MECHANISM_INFO) CE.GetMechanismInfo(slot, CKM.RSA_PKCS);
if(myMechanismInfo.isFlagSet(CK_MECHANISM_INFO.CKF_SIGN))
retArrLst.add(slot);
}
}catch(Exception e){}catch(Error e){}
}
if(retArrLst.size()==0)
throw new Exception("No smartcards found supporting signing with mechanism RSA_PKCS using library " + library);
long[] ret = new long[retArrLst.size()];
for(int i=0;i getCertificateList(long slotID) throws Exception{
ArrayList ret = new ArrayList();
long sessionID = CE.OpenSession(slotID, (CK_SESSION_INFO.CKF_RW_SESSION | CK_SESSION_INFO.CKF_SERIAL_SESSION), null, null);
try {
long[] objectIdList = CE.FindObjects(sessionID, new CKA[]{ new CKA(CKA.CLASS, CKO.CERTIFICATE)});
for(long objectId:objectIdList){
CKA[] ckaId = new CKA[]{ new CKA(CKA.ID, new byte[255])};
CE.GetAttributeValue(sessionID, objectId, ckaId);
byte[] id = StringUtils.trim(ckaId[0].getValue());
CKA[] ckaLabel = new CKA[]{ new CKA(CKA.LABEL, new byte[255])};
CE.GetAttributeValue(sessionID, objectId, ckaLabel);
byte[] label = StringUtils.trim(ckaLabel[0].getValue());
CKA[] ckaValue = new CKA[]{ new CKA(CKA.VALUE, new byte[2048])};
CE.GetAttributeValue(sessionID, objectId, ckaValue);
X509Certificate cert = X509Utils.getX509Certificate(ckaValue[0].getValue());
if(!(cert.getKeyUsage()[0] || cert.getKeyUsage()[1]))
continue;
CertificateData cd = new CertificateData();
cd.certID = id;
cd.certLABEL = label;
cd.cert = cert;
ret.add(cd);
}
return ret;
} finally {
CE.CloseSession(sessionID);
}
}
public long login(long slotID, String pin) throws Exception, Error{
long session = CE.OpenSession(slotID, (CK_SESSION_INFO.CKF_RW_SESSION | CK_SESSION_INFO.CKF_SERIAL_SESSION), null, null);
CK_TOKEN_INFO tokenInfo = (CK_TOKEN_INFO) CE.GetTokenInfo(slotID);
if(tokenInfo.isFlagSet(CK_TOKEN_INFO.CKF_LOGIN_REQUIRED)){
if(tokenInfo.isFlagSet(CK_TOKEN_INFO.CKF_PROTECTED_AUTHENTICATION_PATH))
CE.Login(session, CKU.USER, null);
else
CE.Login(session, CKU.USER, pin.getBytes());
}
return session;
}
public byte[] signData(long sessionID, byte[] certId, byte[] certLabel, byte[] data) throws Exception, Error{
long[] privateKeyObjectIdList = CE.FindObjects(sessionID, new CKA[]{ new CKA(CKA.CLASS, CKO.PRIVATE_KEY)});
long privateKeyObjectIdToUse = -1;
for(long privateKeyObjectId : privateKeyObjectIdList) {
CKA[] ckaId = new CKA[]{ new CKA(CKA.ID, new byte[255])};
CE.GetAttributeValue(sessionID, privateKeyObjectId, ckaId);
byte[] id = StringUtils.trim(ckaId[0].getValue());
CKA[] ckaLabel = new CKA[]{ new CKA(CKA.LABEL, new byte[255])};
CE.GetAttributeValue(sessionID, privateKeyObjectId, ckaLabel);
byte[] label = StringUtils.trim(ckaLabel[0].getValue());
if(Arrays.equals(id, certId) || Arrays.equals(label, certLabel))
privateKeyObjectIdToUse = privateKeyObjectId;
}
if(privateKeyObjectIdToUse==-1)
throw new Exception("Impossible to identify a private key using the provided ID or LABEL");
CKA[] ckaSign = new CKA[]{ new CKA(CKA.SIGN, new byte[255])};
CE.GetAttributeValue(sessionID, privateKeyObjectIdToUse, ckaSign);
boolean isForSign = ckaSign[0].getValueBool();
if(!isForSign)
throw new Exception("The identified private key did not support supports signatures with appendix");
byte[] signature = CE.Sign(sessionID, new CKM(CKM.RSA_PKCS, null), privateKeyObjectIdToUse, data);
return signature;
}
public void closeSession(long sessionID){
try{
CE.Logout(sessionID);
}catch(Exception e){}catch(Error e){}
try{
CE.CloseSession(sessionID);
}catch(Exception e){}catch(Error e){}
}
public void disconnectLibrary(){
try{
CE.Finalize();
}catch(Exception e){}catch(Error e){}
}
/*
public static void main(String[] args) {
try{
SmartCardAccessJnaImpl cardManager = new SmartCardAccessJnaImpl();
long[] slotList = cardManager.connectToLibrary("C:\\WINDOWS\\System32\\bit4ipki.dll");
ArrayList certificateDataList = cardManager.getCertificateList(slotList[0]);
long sessionHandle = cardManager.login(slotList[0], "");
CertificateData certificateData = certificateDataList.get(1);
System.out.println(certificateData.cert.getSubjectDN().getName());
byte[] dataTest = "test".getBytes();
byte[] hashToSign = SignUtils.calculateHASH(org.bouncycastle.cms.CMSSignedDataGenerator.DIGEST_SHA256, dataTest);
hashToSign = df.sign.cms.CMSSignedDataWrapper.getDigestInfoToSign(org.bouncycastle.cms.CMSSignedDataGenerator.DIGEST_SHA256, hashToSign);
byte[] signed = cardManager.signData(sessionHandle, certificateData.certID, certificateData.certLABEL, hashToSign);
cardManager.closeSession(sessionHandle);
cardManager.disconnectLibrary();
java.security.Signature sig = java.security.Signature.getInstance("SHA256WithRSA", "BC");
sig.initVerify(certificateData.cert.getPublicKey());
sig.update(dataTest);
System.out.println("Signature verified: " + sig.verify(signed));
}catch(Exception e){e.printStackTrace();}catch(Error e){e.printStackTrace();}
}
*/
}