使用 pkcs#11 从智能卡读取文本文件

Posted

技术标签:

【中文标题】使用 pkcs#11 从智能卡读取文本文件【英文标题】:Read text file from smart card with pkcs#11 【发布时间】:2016-02-25 13:25:47 【问题描述】:

我有一张空白智能卡 (SLE66CX322P, Cardos 4.3b) 和一个读卡器/写卡器 (Gemalto CT 40)。使用软件我可以初始化卡、生成密钥、证书等。我还可以创建一个简单的 ASCII 文件,其中包含一些将存储在智能卡上的文本。我的问题是,如何使用pkcs#11 API 读取C/C++ 中此ASCII 文件的内容?

这是我迄今为止尝试过的(也使用 Qt):

---------- cardreader.h ------------

#ifndef CARDREADER_H
#define CARDREADER_H

#include "cm-pkcs11.h"
#include <QCoreApplication>
#include <QObject>
#include <QtDebug>

class CardReader: public QObject

    Q_OBJECT

private:

    CK_RV rv;
    CK_ULONG slotCount;
    CK_SLOT_ID slotIds[10];
    CK_SLOT_ID slotId;
    CK_SESSION_HANDLE session;
    CK_TOKEN_INFO_PTR info;

    void readCard();

public:

    explicit CardReader(QObject *parent = 0);
;

#endif // CARDREADER_H

------- cardreader.cpp ----------

#include "cardreader.h"

CardReader::CardReader(QObject *parent) : QObject(parent)

    readCard();


/***********************************************************************/

void CardReader::readCard()

    rv = C_Initialize(NULL_PTR);

    slotCount = 10;
    rv = C_GetSlotList(CK_TRUE, slotIds, &slotCount);
    qWarning() << "Found" << slotCount << "slots";

    if (rv != CKR_OK || slotCount < 1)
    
        qWarning() << "No slots found -> exit";
        return;
    

    slotId = slotIds[0];

    rv = C_OpenSession(slotId, CKF_SERIAL_SESSION|CKF_RW_SESSION, NULL_PTR, NULL_PTR, &session);

    if (rv != CKR_OK)
    
        qWarning() << "Sessions could not be opened -> exit";
        qWarning() << "RV (as hex value) = " << QString("%1").arg(rv, 0, 16);
        return;
    

    /*********************************************************************/

    CK_OBJECT_CLASS dataClass = CKO_DATA;
    CK_OBJECT_HANDLE handleObject;
    CK_UTF8CHAR label[] = "MyLabel";
    CK_ULONG ulCount = 4ul;
    CK_CHAR application[] = "TestApplication";
    CK_BYTE dataValue[] = "MyData";
    CK_BBOOL valid = CK_TRUE;
    CK_ATTRIBUTE dataTemp[] =
    
        CKA_CLASS, &dataClass, sizeof(dataClass),
        CKA_VALUE, dataValue, sizeof(dataValue),
        CKA_LABEL, label, sizeof(label)-1,
        //CKA_APPLICATION, application, sizeof(application)
        CKA_TOKEN, &valid, sizeof(true)
    ;

    rv = C_FindObjectsInit(session, dataTemp, 0);
    if (rv != CKR_OK)
    
        qWarning() << "C_FindObjectsInit Error -> exit";
        qWarning() << "C_FindObjectsInit Error" << QString("%1").arg(rv, 0, 16);
        return;
    


    while (1)
    
        rv = C_FindObjects(session, &handleObject, 1, &ulCount);
        qWarning() << "C_FindObjects Result =" << QString("%1").arg(rv, 0, 16) << ", count =" << ulCount;
        if (rv != CKR_OK || ulCount == 0)
            break;

        rv = C_GetAttributeValue(session, handleObject, dataTemp, ulCount);

        if (rv != CKR_OK)
        
            qWarning() << "C_GetAttributeValue error -> exit";
            qWarning() << "RV (as hex value) = " << QString("%1").arg(rv, 0, 16) << ", count = " << ulCount;
            return;
        

        qWarning() << (const char *) dataTemp[0].pValue;
        qWarning() << (const char *) dataTemp[1].pValue;
        qWarning() << (const char *) dataTemp[2].pValue;
        qWarning() << (const char *) dataTemp[3].pValue;
    

---------- ma​​in.cpp ----------

#include "cardreader.h"

int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);
    CardReader c;

    return a.exec();

结果是块

        qWarning() << (const char *) dataTemp[0].pValue;
        qWarning() << (const char *) dataTemp[1].pValue;
        qWarning() << (const char *) dataTemp[2].pValue;
        qWarning() << (const char *) dataTemp[3].pValue;

在控制台中打印了四次,但总是以MyLabelMyData 作为我在CardReader::readCard() 开头指定的结果。智能卡上的文本文件的内容当然是不同的,但遗憾的是没有显示在输出中。

我还想提一下,我还能够使用C_CreateObject() 函数。这在智能卡上生成了第二个 ASCII 文件。但是我的代码也无法读取第二个文件。

【问题讨论】:

【参考方案1】:

最可能的原因:

您的ulCount 变量被C_FindObjects() 覆盖为1。 在调用C_GetAttributeValue之前,您需要再次将其重新分配给4。 这样您的代码只读取模板中的第一个属性。

一些额外的(随机的)注释:

由于您不调用 C_Login(),因此您只能查看/访问公共对象(设置为 CKA_PRIVATE 的对象为您隐藏)。 C_FindObjectsInit() 的零 ulCount 参数会导致您的代码枚举所有令牌对象 - 您可能希望 CKA_CLASSCKA_LABEL 过滤器有效。 我建议不要对C_FindObjectsInit()C_GetAttributeValue() 使用相同的模板 您的 dataValue 缓冲区非常小 -- 您确定该值适合吗?

祝你好运!

【讨论】:

谢谢,你的提示解决了。我已将4 重新分配给ulCount,将C_FindObjectsInit() 的最后一个参数更改为1,并增加了dataValuelabel 的缓冲区。但是,我不确定C_FindObjectsInit() 中的CK_ULONG 值是做什么的。当我输入 2 时,我没有得到任何结果。通过1,我得到了我的芯片卡的内容。 0 给了我更多不可读字符的结果。我认为CK_ULONG 值告诉我文本文件有多少属性必须适合模板,以及两个属性。适合:dataValuelabel @Varius C_FindObjectsInit()CK_ULONG ulCount 参数定义了搜索模板中匹配的属性(及其值)的数量。在您的情况下,0="所有对象"、1="CKA_CLASS==CKO_DATA 的对象"和 2="CKA_CLASS==CKO_DATA 和 CKA_VALUE==MyData 的对象"。由于您的数据对象可能不包含“MyData”,因此您的搜索不会为您提供ulCount=2 的结果。这有意义吗? 此外,您不应将属性值 (pValue) 视为以零结尾的字符串。您应该使用valueLen 字段来确定输出长度并仅转储有效字节。 并且您应该在调用C_GetAttributeValue()之前重置模板中的所有字段valueLen 好的,谢谢。现在这是有道理的。事实上,我的卡上没有任何带有MyData 的文件。所以ulCount=1 给了我所有CKO_DATA 类型的文件,独立于它们的CKA_VALUE。而ulCount=2 会给我所有CKO_DATA 类型的文件和“MyData”的值(如果我在卡上有这样的文件......)。现在明白了。谢谢

以上是关于使用 pkcs#11 从智能卡读取文本文件的主要内容,如果未能解决你的问题,请参考以下文章

Excel VBA 如何从 Web 读取文本文件(未缓存)?

想从文本文件中读取整数

使用正则表达式中的 CSS 突出显示从文件中读取的文本的语法

从Java中的图像文件中读取文本[重复]

如何在 Java 中真正从类路径中读取文本文件

如何在 Java 中真正从类路径中读取文本文件