OVH Cloud OVH Cloud

cryptlib

10 réponses
Avatar
yann
Bonjour tous
est ce que quelqu'un aurait des exemples de code en C pour signer des
fichiers avec la cryptlib à l'aide d'un token USB ????

j'arrive à me connecter au token en PKCS#11 mais je n'ai pas encore trouvé
la façon de faire pour en exploiter les clefs afin de signer des fichiers.

si ma question n'est pas assez claire, demandez moi j'essayerais de faire
des précisions.

yann

10 réponses

Avatar
pornin
According to yann :
j'arrive à me connecter au token en PKCS#11 mais je n'ai pas encore trouvé
la façon de faire pour en exploiter les clefs afin de signer des fichiers.


En PKCS#11, il faut :
-- initialiser le module PKCS#11 (C_Initialize())
-- ouvrir une session "user" (C_OpenSession())
-- se logguer (C_Login()) (ça dépend du token, mais habituellement les
clés privées ne sont accessibles que lorsqu'on est logué sur le token)
-- obtenir un handle sur la clé privée à coups de C_FindObjectsInit(),
C_FindObjects() et C_FindObjectsFinal()
-- faire la signature : C_SignInit() puis C_Sign()
-- fermer la porte en sortant : C_CloseAllSessions() puis C_Finalize()

Pour les détails, il faut faire un peu de C_GetSlotList() (pour avoir la
liste des "slots" présents -- il faut un "slot ID" pour C_OpenSession())
et de C_GetTokenInfo() pour savoir comment faire le C_Login() (en gros,
il faut regarder les flags : si CKF_LOGIN_REQUIRED est présent, il faut
faire un C_Login() ; si CKF_PROTECTED_AUTHENTICATION_PATH est absent, il
faut aussi fournir un code PIN, demandé à l'utilisateur, dont la taille
est indiquée dans la structure remplie par C_GetTokenInfo()).

Il reste la question de quelle clé choisir quand il y en a plusieurs.
L'usage le plus répandu consiste à stocker sur le token un certificat
X.509 et sa clé privée, avec la même valeur de l'attribut CKA_ID pour
les deux. Comme ça, on énumère les certificats, on choisit celui qu'on
veut, on récupère son CKA_ID et on cherche les clés privées selon ce
CKA_ID.


--Thomas Pornin

Avatar
pornin
According to Thomas Pornin :
En PKCS#11, il faut :
[snip]


Bon, là, j'ai raconté comment ça se passe en PKCS#11 directement,
indépendamment de cryptlib. Cryptlib doit probablement fournir une
interface au-dessus de tout cela, et je ne connais pas cette interface.


--Thomas Pornin

Avatar
Erwann ABALEA
On Thu, 4 Dec 2003, Thomas Pornin wrote:

According to yann :
j'arrive à me connecter au token en PKCS#11 mais je n'ai pas encore trouvé
la façon de faire pour en exploiter les clefs afin de signer des fichiers.


En PKCS#11, il faut :
-- initialiser le module PKCS#11 (C_Initialize())
-- ouvrir une session "user" (C_OpenSession())
-- se logguer (C_Login()) (ça dépend du token, mais habituellement les
clés privées ne sont accessibles que lorsqu'on est logué sur le token)
-- obtenir un handle sur la clé privée à coups de C_FindObjectsInit(),
C_FindObjects() et C_FindObjectsFinal()
-- faire la signature : C_SignInit() puis C_Sign()
-- fermer la porte en sortant : C_CloseAllSessions() puis C_Finalize()


Beurk. Un C_CloseSession() suivi d'un C_Finalize() suffiront, pas la peine
de fermer la porte de tes voisins... ;)

Pour les détails, il faut faire un peu de C_GetSlotList() (pour avoir la
liste des "slots" présents -- il faut un "slot ID" pour C_OpenSession())


Et les slots ne sont pas numérotés sur la même base, donc pas moyen de
sauter cette étape.

et de C_GetTokenInfo() pour savoir comment faire le C_Login() (en gros,
il faut regarder les flags : si CKF_LOGIN_REQUIRED est présent, il faut
faire un C_Login() ; si CKF_PROTECTED_AUTHENTICATION_PATH est absent, il
faut aussi fournir un code PIN, demandé à l'utilisateur, dont la taille
est indiquée dans la structure remplie par C_GetTokenInfo()).


Et en espérant que le constructeur n'ait pas intégré de mécanisme
d'authentification plus poussé que le PKCS#11 standard, qui est quand même
très faible. Sur 2 des tokens que nous utilisons ici, il y a une
authentification externe, par secrets partagés stockés sur support
matériel. Sur l'un de ces tokens, c'est 'transparent' pour l'application,
pour l'autre non.

Il reste la question de quelle clé choisir quand il y en a plusieurs.
L'usage le plus répandu consiste à stocker sur le token un certificat
X.509 et sa clé privée, avec la même valeur de l'attribut CKA_ID pour
les deux. Comme ça, on énumère les certificats, on choisit celui qu'on
veut, on récupère son CKA_ID et on cherche les clés privées selon ce
CKA_ID.


Et certains tokens ne supportent pas le stockage d'autre chose qu'une clé
(privée, secrète, ...). Dans ce cas, il faut ajuster en conséquence.

--
Erwann ABALEA - RSA PGP Key ID: 0x2D0EABD5
-----
Ce ne sont que des propositions. Je ne veux pas les faire passer en
force. Je pense que si mes idées doivent être reprises, elles ne
doivent pas passer au vote, pour plusieurs raison :
-+- BC in : http://neuneu.ctw.cc - Neuneu sans vote et sans forcer -+-


Avatar
pornin
According to Erwann ABALEA :
Beurk. Un C_CloseSession() suivi d'un C_Finalize() suffiront, pas la peine
de fermer la porte de tes voisins... ;)


Ça ne change rien. Le C_CloseAllSessions() et/ou le C_CloseSession() sont
cosmétiques, puisque le C_Finalize() coupe tout de toutes façons. Par
ailleurs, la portée du C_CloseAllSessions() et celle du C_Finalize() sont
identique : toute l'application, rien que l'application. Normalement, ça
n'influe pas sur les autres applications qui utilisent le même token en
même temps.

(Enfin, en théorie. En pratique, j'imagine bien que l'accès simultané
par plusieurs applications n'est pas très bien géré sur la plupart des
tokens existants.)


Et en espérant que le constructeur n'ait pas intégré de mécanisme
d'authentification plus poussé que le PKCS#11 standard, qui est quand
même très faible.


C'est normalement transparent : si le token sait faire de
l'authentification autre que le code PIN, il la fait et puis basta. Le
flag CKF_PROTECTED_AUTHENTICATION_PATH permet d'indiquer à l'application
qu'il n'y a pas besoin de code PIN parce que l'authentification faite
par ailleurs suffit.

PKCS#11 est une API pour parler à des tokens cryptographiques, mais rien
n'oblige les tokens à ne s'exprimer _que_ par cette API.

Évidemment, il y a des applications buggués (Netscape...) qui imaginent
autrement.


--Thomas Pornin

Avatar
Erwann ABALEA
On Thu, 4 Dec 2003, Thomas Pornin wrote:

According to Erwann ABALEA :
Beurk. Un C_CloseSession() suivi d'un C_Finalize() suffiront, pas la peine
de fermer la porte de tes voisins... ;)


Ça ne change rien. Le C_CloseAllSessions() et/ou le C_CloseSession() sont
cosmétiques, puisque le C_Finalize() coupe tout de toutes façons. Par


Non. Le C_Finalize() ne coupe pas les autres sessions ouvertes sur le
token (je ne parle pas des sessions ouvertes par le même programme, qui
sont effectivement coupées à l'appel de C_Finalize()).
C_CloseAllSessions() coupe *toutes* les sessions ouvertes, mêmes celles
que *tu* n'as pas ouvertes.

ailleurs, la portée du C_CloseAllSessions() et celle du C_Finalize() sont
identique : toute l'application, rien que l'application. Normalement, ça
n'influe pas sur les autres applications qui utilisent le même token en
même temps.


Faux. J'utilisais parfois un C_CloseAllSessions() pour fermer les sessions
déclarées ouvertes par un programme qui avait planté (et donc qui ne
libérait rien).

Et en espérant que le constructeur n'ait pas intégré de mécanisme
d'authentification plus poussé que le PKCS#11 standard, qui est quand
même très faible.


C'est normalement transparent : si le token sait faire de
l'authentification autre que le code PIN, il la fait et puis basta. Le
flag CKF_PROTECTED_AUTHENTICATION_PATH permet d'indiquer à l'application
qu'il n'y a pas besoin de code PIN parce que l'authentification faite
par ailleurs suffit.


J'ai ici 2 tokens qui ont un comportement différent. Le premier n'offre
que la fonction C_Login() pour s'authentifier, mais cet appel ne sert à
rien (globalement), puisque l'essentiel de l'authentification est
effectuée par le token lui-même (au démarrage, il demande l'insertion de
secrets pour être activé). Dans ce cas, l'application ne sait pas qu'il
faut autre chose que le C_Login(). Mais ça n'est pas génant, puisque si
l'utilisateur n'a pas inséré ses secrets, la carte ne répond pas, et donc
le programme reste bloqué dès le C_OpenSession().

Le deuxième token offre une deuxième fonction (dont je n'ai pas le nom
ici), fonction qui réalise la même chose, à savoir dire au token qu'il
doit demander un ensemble de secrets partagés. Une application qui n'a pas
connaissance de cela ne pourra pas utiliser les clés privées contenues
dans ce token.

Évidemment, il y a des applications buggués (Netscape...) qui imaginent
autrement.


Et Netscape fait bien pire encore:
- tentative d'insertion d'une clé secrète dès qu'un token déclare qu'il
sait faire un cryptosystème symétrique (probablement 'pour voir'),
- ouverture de 2 sessions dont une en read-only, et effacement d'une clé
publique après génération d'une clé privée *en utilisant la session
read-only*,
- de temps en temps, avant de signer ou déchiffrer quelque chose,
Netscape fait signer un texte bidon (avec un pattern en texte), là
encore, pour voir...

Netscape est très crade de ce point de vue... :(

--
Erwann ABALEA - RSA PGP Key ID: 0x2D0EABD5
-----
je ne parle pas de secte moi, je parle d'extraterrestres et de
religions.
-+- Ant1 in Guide du neuneu Usenet : David Vincent les a vus -+-


Avatar
Jean-Marc Desperrier
Erwann ABALEA wrote:
Et Netscape fait bien pire encore:


Netscape _4_ fait cela.
On parle là d'une appli qui n'a subi aucune refonte significative depuis
1997.

C'est Mozilla ou les Netscape 7.x qui représentent l'état actuel.

Avatar
Erwann ABALEA
On Fri, 5 Dec 2003, Jean-Marc Desperrier wrote:

Erwann ABALEA wrote:
Et Netscape fait bien pire encore:


Netscape _4_ fait cela.
On parle là d'une appli qui n'a subi aucune refonte significative depuis
1997.

C'est Mozilla ou les Netscape 7.x qui représentent l'état actuel.


Je n'ai pas testé si ces cochonneries étaient encore là sur les versions
plus récentes de Netscape/Mozilla.

Je regarderai ça à l'occasion, j'aime bien avoir mes certificats
personnels sur une Luna. Ca fait 'riche'... ;) C'est pour ça que j'ai
écrit une couche PKCS#11 entre Netscape et la bibliothèque PKCS#11 fournie
par Chrysalis (je pense qu'ils savent ce qu'ils font, le Directeur
Technique de Chrysalis-ITS travaillait auparavant chez RSA, et il a fait
partie de l'équipe qui a pondu le PKCS#11 v1).

--
Erwann ABALEA - RSA PGP Key ID: 0x2D0EABD5
-----
Ce que je comprends pas, c'est pourquoi leurs votes compte plus ?
Et surtout, ca leur rapporte QUOI ????
Putain faut vraiment être CON, grave.
-+- A in GNU - La Cabale, c'est plus FORT que toi ! -+-


Avatar
pornin
According to Erwann ABALEA :
Non. Le C_Finalize() ne coupe pas les autres sessions ouvertes sur le
token (je ne parle pas des sessions ouvertes par le même programme, qui
sont effectivement coupées à l'appel de C_Finalize()).
C_CloseAllSessions() coupe *toutes* les sessions ouvertes, mêmes celles
que *tu* n'as pas ouvertes.


On n'a pas lu le même PKCS#11 alors. Dans celui qui se télécharge
sur le site web de RSA Labs, il est bien spécifié que les sessions
sont spécifiques aux applications, et que le C_CloseAllSessions()
ferme toutes les sessions _de l'application qui appelle le
C_CloseAllSessions()_. L'exemple détaillé dans la section 6.6.7 est très
instructif de ce point de vue. On voit une application tenter d'accéder
à une session d'une autre application, et se faire jeter (paragraphe 8 :
« The attempt fails, because A and B have no access rights to each
other's sessions or objects. »). Plus loin (paragraphe 26), une
application appelle C_CloseAllSessions() et ça ne ferme pas les sessions
des autres applications. La spécification de C_CloseAllSessions() en
rajoute une couche (page 159) : « C_CloseAllSessions closes all sessions
an application has with a token. slotID specifies the tokents slot. »

Enfin, la section 11.6, page 156, décrit en quelques points comment les
choses devraient raisonnablement se passer :

<<
A typical application might perform the following series of steps to
make use of a token (note that there are other reasonable sequences of
events that an application might perform):

1. Select a token.

2. Make one or more calls to C_OpenSession to obtain one or more
sessions with the token.

3. Call C_Login to log the user into the token. Since all sessions an
application has with a token have a shared login state, C_Login only
needs to be called for one of the sessions.

4. Perform cryptographic operations using the sessions with the token.

5. Call C_CloseSession once for each session that the application
has with the token, or call C_CloseAllSessions to close all the
applicationas sessions simultaneously.

As has been observed, an application may have concurrent sessions with
more than one token. It is also possible for a token to have concurrent
sessions with more than one application.





Faux. J'utilisais parfois un C_CloseAllSessions() pour fermer les sessions
déclarées ouvertes par un programme qui avait planté (et donc qui ne
libérait rien).


Appels non conformes au standard pour obtenir un résultat lui aussi non
conforme au standard. Il n'y a pas de quoi en tirer grande gloire, ni
une loi générale sur le comportement des modules PKCS#11 existants. Ça
veut juste dire que tu avais sous la main un module PKCS#11 buggué et
que tu contournais lesdits bugs.


--Thomas Pornin


Avatar
Erwann ABALEA
On Sun, 7 Dec 2003, Thomas Pornin wrote:

According to Erwann ABALEA :
Non. Le C_Finalize() ne coupe pas les autres sessions ouvertes sur le
token (je ne parle pas des sessions ouvertes par le même programme, qui
sont effectivement coupées à l'appel de C_Finalize()).
C_CloseAllSessions() coupe *toutes* les sessions ouvertes, mêmes celles
que *tu* n'as pas ouvertes.


On n'a pas lu le même PKCS#11 alors.


Probable.

Dans celui qui se télécharge
sur le site web de RSA Labs,


Quelle version? Il y a la 1.0, 2.0 (non supportée), 2.01, et 2.10. :)

il est bien spécifié que les sessions
sont spécifiques aux applications, et que le C_CloseAllSessions()
ferme toutes les sessions _de l'application qui appelle le
C_CloseAllSessions()_. L'exemple détaillé dans la section 6.6.7 est très
instructif de ce point de vue. On voit une application tenter d'accéder
à une session d'une autre application, et se faire jeter (paragraphe 8 :
« The attempt fails, because A and B have no access rights to each
other's sessions or objects. »). Plus loin (paragraphe 26), une
application appelle C_CloseAllSessions() et ça ne ferme pas les sessions
des autres applications.


Ca, c'est bon pour le PKCS#11 v2.0 et au-delà. Rien de tel en PKCS#11v1.0.
Effectivement, il n'existe pas énormément de tokens avec une bibliothèque
PKCS#11v1, mais il se trouve que j'en ai ici (et même la v1 avant l'errata
qui inverse l'ordre des handle clé publique/privée dans
C_GenerateKeyPair()). Et il me semble (sans l'avoir vérifié) que ce
comportement (C_CloseAllSessions() ferme *toutes* les sessions) a perduré
dans la bibliothèque v2 du même token. Si j'ai le temps (j'en doute),
j'essaierai.

En passant, j'ai regardé le 'PKCS#11 Conformance Profile Specification',
et ce comportement n'est pas mentionné. Ce document est très succint, donc
peut-être pas approprié.

Je viens également de regarder rapidement le code de GPKCS11, accessible
depuis la même page par le lien 'Reference implementations', et je n'ai
pas vu de vérification lors de la fermeture d'une session ou de plusieurs
sessions. D'un autre côté, GPKCS11 est du pur soft, et je ne suis pas
certain que cette bibliothèque implémente une abstraction d'un token
matériel, donc là encore, ça n'est peut-être pas approprié.

La spécification de C_CloseAllSessions() en
rajoute une couche (page 159) : « C_CloseAllSessions closes all sessions
an application has with a token. slotID specifies the tokents slot. »


Là, c'est pas flagrant, puisque c'est le même texte qu'en v1, et que le
comportement en v1 est différent. Disons que sans autre précision, on peut
interpréter cette description différemment.

Enfin, la section 11.6, page 156, décrit en quelques points comment les
choses devraient raisonnablement se passer :
[...]

5. Call C_CloseSession once for each session that the application
has with the token, or call C_CloseAllSessions to close all the
applicationas sessions simultaneously.

As has been observed, an application may have concurrent sessions with
more than one token. It is also possible for a token to have concurrent
sessions with more than one application.


Là d'accord, c'est clairement spécifié, le développeur sait à quoi
s'attendre. Mais c'est bien dans les 'nouvelles' versions du standard...

Appels non conformes au standard pour obtenir un résultat lui aussi non
conforme au standard.


Le 'standard' PKCS#11 est tellement changeant et incomplet que.... ;)

--
Erwann ABALEA - RSA PGP Key ID: 0x2D0EABD5
-----
C'est impossible que la majorité ait voté pour détruire le frjv sans
créer un ou des autres NG. C'est incompréhensible...
-+- AS in GNU : et pis d'abord les dinosaures y existent même pas -+-


Avatar
pornin
According to Erwann ABALEA :
Quelle version? Il y a la 1.0, 2.0 (non supportée), 2.01, et 2.10. :)


Ben... la 2.11 "revision 1" de novembre 2001. Le "PKCS#11 conformance
profile specification" parle explicitement du 2.10 ("The following
additional APIs as defined in PKCS v2.10 must be supported.")

Dans mon cas, je suis du côté "token" donc la situation n'est pas la
même (je dois contourner les bugs de Netscape et de Lotus Notes plutôt
que ceux des tokens existants).


--Thomas Pornin