libdigidocpp
PKCS11Signer.cpp
Go to the documentation of this file.
1 /*
2  * libdigidocpp
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  */
19 
20 #include "PKCS11Signer.h"
21 
22 #include "pkcs11.h"
23 
24 #include "../../log.h"
25 #include "../../Conf.h"
26 #include "../../util/File.h"
27 #include "../cert/X509Cert.h"
28 
29 #include <sstream>
30 #include <string.h>
31 #ifdef _WIN32
32 #include <Windows.h>
33 #else
34 #include <dlfcn.h>
35 #endif
36 
37 namespace digidoc
38 {
39 
40 struct SignSlot
41 {
43  CK_SLOT_ID slot;
45 };
46 
48 {
49 public:
51  : f(0)
52  , slots(0)
53  , numberOfSlots(0)
54  {
55  sign.slot = 0;
56  sign.cert = 0;
57  };
58 
60  bool attribute( CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj,
61  CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG &size ) const;
62  std::vector<CK_OBJECT_HANDLE> findObject( CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls ) const;
63 
64 #ifdef _WIN32
65  bool load(const std::string &driver)
66  {
67  std::wstring _driver = util::File::encodeName(driver);
68  return (h = LoadLibraryW(_driver.c_str())) != 0;
69  }
70 
71  void* resolve(const char *symbol)
72  { return h ? (void*)GetProcAddress(h, symbol) : 0; }
73 
74  void unload()
75  { if(h) FreeLibrary(h); h = 0; }
76 #else
77  bool load(const std::string &driver)
78  { return (h = dlopen(driver.c_str(), RTLD_LAZY)); }
79 
80  void* resolve(const char *symbol)
81  { return h ? dlsym(h, symbol) : 0; }
82 
83  void unload()
84  { if(h) dlclose(h); h = 0; }
85 #endif
86 
88 #ifdef _WIN32
89  HINSTANCE h;
90 #else
91  void *h;
92 #endif
93 
97 
98  static const unsigned char sha1[];
99  static const unsigned char sha224[];
100  static const unsigned char sha256[];
101  static const unsigned char sha384[];
102  static const unsigned char sha512[];
103 };
104 
105 }
106 
107 using namespace digidoc;
108 
109 const unsigned char PKCS11SignerPrivate::sha1[] =
110 { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
111 
112 const unsigned char PKCS11SignerPrivate::sha224[] =
113 { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };
114 
115 const unsigned char PKCS11SignerPrivate::sha256[] =
116 { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
117 
118 const unsigned char PKCS11SignerPrivate::sha384[] =
119 { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
120 
121 const unsigned char PKCS11SignerPrivate::sha512[] =
122 { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
123 
124 
125 
126 bool PKCS11SignerPrivate::attribute( CK_SESSION_HANDLE session,
127  CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG &size ) const
128 {
129  CK_ATTRIBUTE attr = { type, value, size };
130  CK_RV err = f->C_GetAttributeValue( session, obj, &attr, 1 );
131  size = attr.ulValueLen;
132  return err == CKR_OK;
133 }
134 
135 std::vector<CK_OBJECT_HANDLE> PKCS11SignerPrivate::findObject( CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls ) const
136 {
137  CK_ATTRIBUTE attr = { CKA_CLASS, &cls, sizeof(cls) };
138  if( f->C_FindObjectsInit( session, &attr, 1 ) != CKR_OK )
139  return std::vector<CK_OBJECT_HANDLE>();
140 
141  CK_ULONG count = 32;
142  std::vector<CK_OBJECT_HANDLE> result( count );
143  CK_RV err = f->C_FindObjects( session, &result[0], result.size(), &count );
144  result.resize( err == CKR_OK ? count : 0 );
145  f->C_FindObjectsFinal( session );
146  return result;
147 }
148 
157  : d(new PKCS11SignerPrivate)
158 {
159  loadDriver(Conf::getInstance()->getPKCS11DriverPath());
160 }
161 
169 PKCS11Signer::PKCS11Signer(const std::string &driver) throw(SignException)
170  : d(new PKCS11SignerPrivate)
171 {
172  loadDriver(driver);
173 }
174 
179 {
180  DEBUG("~PKCS11Signer()");
181  unloadDriver();
182  delete d;
183 }
184 
186 {
187  if(!d->f)
188  return;
189 
190  if(!d->slots)
191  {
192  // Release all slots.
193  delete [] d->slots;
194  d->slots = 0;
195  d->numberOfSlots = 0;
196  }
197 
198  // Release PKCS #11 context.
199  d->f->C_Finalize( 0 );
200  d->f = 0;
201  d->unload();
202 }
203 
211 void PKCS11Signer::loadDriver(const std::string &driver) throw(SignException)
212 {
213  DEBUG("PKCS11Signer(driver = '%s')", driver.c_str());
214  if(driver.empty())
215  THROW_SIGNEXCEPTION("Failed to load driver for PKCS #11 engine: %s.", driver.c_str());
216 
217  unloadDriver();
218 
219  if(!d->load(driver))
220  THROW_SIGNEXCEPTION("Failed to load driver for PKCS #11 engine: %s.", driver.c_str());
221 
222  CK_C_GetFunctionList l = CK_C_GetFunctionList(d->resolve( "C_GetFunctionList" ));
223  if( !l ||
224  l( &d->f ) != CKR_OK ||
225  d->f->C_Initialize( 0 ) != CKR_OK )
226  THROW_SIGNEXCEPTION("Failed to load driver for PKCS #11 engine: %s.", driver.c_str());
227 }
228 
230 {
231  if(Conf::getInstance())
232  loadDriver(Conf::getInstance()->getPKCS11DriverPath());
233 }
234 
246 {
247  DEBUG("PKCS11Signer::getCert()");
248 
249  // If certificate is already selected return it.
250  if(d->sign.certificate.handle())
251  return d->sign.certificate.handle();
252 
253  // Set selected state to 'no certificate selected'.
254  d->sign.slot = 0;
255  if(d->slots)
256  {
257  // Release all slots.
258  delete [] d->slots;
259  d->slots = 0;
260  d->numberOfSlots = 0;
261  }
262 
263  // Load all slots.
264  if(d->f->C_GetSlotList(true, 0, &d->numberOfSlots) != CKR_OK)
265  THROW_SIGNEXCEPTION("Could not find any ID-Cards in any readers");
266  d->slots = new CK_SLOT_ID[d->numberOfSlots];
267  if(d->f->C_GetSlotList(true, d->slots, &d->numberOfSlots) != CKR_OK)
268  THROW_SIGNEXCEPTION("Could not find any ID-Cards in any readers");
269 
270  // Iterate over all found slots, if the slot has a token, check if the token has any certificates.
271  CK_SESSION_HANDLE session = 0;
272  std::vector<PKCS11Signer::PKCS11Cert> certificates;
273  std::vector<SignSlot> certSlotMapping;
274  for(CK_ULONG i = 0; i < d->numberOfSlots; ++i)
275  {
276  CK_TOKEN_INFO token;
277  std::vector<CK_OBJECT_HANDLE> objs;
278 
279  if(session)
280  d->f->C_CloseSession(session);
281 
282  if(d->f->C_GetTokenInfo(d->slots[i], &token) != CKR_OK ||
283  d->f->C_OpenSession(d->slots[i], CKF_SERIAL_SESSION, 0, 0, &session) != CKR_OK ||
284  (objs = d->findObject(session, CKO_CERTIFICATE)).empty())
285  continue;
286 
287  for(CK_ULONG j = 0; j < objs.size(); ++j)
288  {
289  CK_ULONG size = 0;
290  if(!d->attribute(session, objs[j], CKA_VALUE, 0, size))
291  continue;
292  std::vector<unsigned char> value(size, 0);
293  if(!d->attribute(session, objs[j], CKA_VALUE, &value[0], size))
294  continue;
295  X509Cert x509( value );
296  if(!x509.isValid())
297  continue;
298  SignSlot signSlot = { x509, d->slots[i], j };
299  certSlotMapping.push_back(signSlot);
300  certificates.push_back(d->createPKCS11Cert(&token, x509));
301  }
302  }
303  if(session)
304  d->f->C_CloseSession(session);
305 
306  if(certificates.empty())
307  THROW_SIGNEXCEPTION("No certificates found.");
308 
309  // Let the application select the signing certificate.
310  X509Cert selectedCert = selectSigningCertificate(certificates).cert;
311  if(!selectedCert.handle())
312  THROW_SIGNEXCEPTION("No certificate selected.");
313 
314  // Find the corresponding slot and PKCS11 certificate struct.
315  for(std::vector<SignSlot>::const_iterator i = certSlotMapping.begin(); i != certSlotMapping.end(); ++i)
316  {
317  if(!d->sign.cert && i->certificate == selectedCert)
318  d->sign = *i;
319  }
320 
321  if(!d->sign.certificate.handle())
322  THROW_SIGNEXCEPTION("Could not find slot for selected certificate.");
323 
324  return d->sign.certificate.handle();
325 }
326 
336 void PKCS11Signer::sign(const Digest &digest, Signature &signature) throw(SignException)
337 {
338  DEBUG("sign(digest = {type=%s,digest=%p,length=%d}, signature={signature=%p,length=%d})",
339  OBJ_nid2sn(digest.type), digest.digest, digest.length, signature.signature, signature.length);
340 
341  // Check that sign slot and certificate are selected.
342  if(!d->sign.certificate.handle())
343  THROW_SIGNEXCEPTION("Signing slot or certificate are not selected.");
344 
345  // Login if required.
346  CK_TOKEN_INFO token;
347  CK_SESSION_HANDLE session;
348  if(d->f->C_GetTokenInfo(d->sign.slot, &token) != CKR_OK ||
349  d->f->C_OpenSession(d->sign.slot, CKF_SERIAL_SESSION, 0, 0, &session) != CKR_OK)
350  THROW_SIGNEXCEPTION("Signing slot or certificate are not selected.");
351 
352  if(token.flags & CKF_LOGIN_REQUIRED)
353  {
354  int rv = CKR_OK;
356  rv = d->f->C_Login(session, CKU_USER, 0, 0);
357  else
358  {
359  std::string pin = getPin(d->createPKCS11Cert(&token, d->sign.certificate));
360  rv = d->f->C_Login(session, CKU_USER, CK_BYTE_PTR(pin.c_str()), CK_ULONG(pin.size()));
361  }
362  switch(rv)
363  {
364  case CKR_OK: break;
365  case CKR_CANCEL:
367  {
368  SignException e( __FILE__, __LINE__, "PIN acquisition canceled.");
370  throw e;
371  break;
372  }
373  case CKR_PIN_INCORRECT:
374  {
375  SignException e( __FILE__, __LINE__, "PIN Incorrect" );
377  throw e;
378  break;
379  }
380  case CKR_PIN_LOCKED:
381  {
382  SignException e( __FILE__, __LINE__, "PIN Locked" );
384  throw e;
385  break;
386  }
387  default:
388  std::ostringstream s;
389  s << "Failed to login to token '" << token.label << "': " << rv;
390  SignException e( __FILE__, __LINE__, s.str() );
391  e.setCode( Exception::PINFailed );
392  throw e;
393  break;
394  }
395  }
396 
397  std::vector<CK_OBJECT_HANDLE> key = d->findObject( session, CKO_PRIVATE_KEY );
398  if( key.empty() )
399  THROW_SIGNEXCEPTION("Could not get key that matches selected certificate.");
400 
401  // Sign the digest.
402  CK_MECHANISM mech = { CKM_RSA_PKCS, 0, 0 };
403  if( d->f->C_SignInit( session, &mech, key[d->sign.cert] ) != CKR_OK )
404  THROW_SIGNEXCEPTION("Failed to sign digest");
405 
406  const unsigned char *sha = 0;
407  size_t size = 0;
408  switch( digest.type )
409  {
410  case NID_sha1: sha = PKCS11SignerPrivate::sha1; size = sizeof(PKCS11SignerPrivate::sha1); break;
411  case NID_sha224: sha = PKCS11SignerPrivate::sha224; size = sizeof(PKCS11SignerPrivate::sha224); break;
412  case NID_sha256: sha = PKCS11SignerPrivate::sha256; size = sizeof(PKCS11SignerPrivate::sha256); break;
413  case NID_sha384: sha = PKCS11SignerPrivate::sha384; size = sizeof(PKCS11SignerPrivate::sha384); break;
414  case NID_sha512: sha = PKCS11SignerPrivate::sha512; size = sizeof(PKCS11SignerPrivate::sha512); break;
415  default: break;
416  }
417  unsigned char *data = new unsigned char[size + digest.length];
418  memmove( data, sha, size );
419  memmove( data + size, digest.digest, digest.length );
420  size = size + digest.length;
421 
422  if(d->f->C_Sign(session, data, CK_ULONG(size), 0, CK_ULONG_PTR(&signature.length)) != CKR_OK)
423  {
424  delete [] data;
425  THROW_SIGNEXCEPTION("Failed to sign digest");
426  }
427 
428  signature.signature = new unsigned char[signature.length];
429  CK_RV err = d->f->C_Sign(session, data, CK_ULONG(size), signature.signature, CK_ULONG_PTR(&signature.length));
430  delete [] data;
431  if(err != CKR_OK)
432  THROW_SIGNEXCEPTION("Failed to sign digest");
433 }
434 
443 {
444  PKCS11Signer::PKCS11Cert certificate;
445  certificate.token.label = (char*)token->label;
446  certificate.token.manufacturer = (char*)token->manufacturerID;
447  certificate.token.model = (char*)token->model;
448  certificate.token.serialNr = (char*)token->serialNumber;
449  certificate.label = cert.getSubjectName("CN");
450  certificate.cert = cert;
451  return certificate;
452 }