[xmlsec] Suggestions & Question

Igor Odar igor.odar at eba.si
Sat Aug 20 10:51:44 PDT 2005


Hello!

We are introducing XML signature in our application and we will use 
xmlsec library for that because it is the best there is.
I noticed that mscrypto does not support PEM encoded certificates. I 
suggest the code, that would also work for PEM certificates.

static int xmlSecMSCryptoAppKeysMngrCertLoadMemory( xmlSecKeysMngrPtr 
mngr, const xmlSecByte* data, xmlSecSize dataSize, xmlSecKeyDataFormat 
format, xmlSecKeyDataType type )
{
    xmlSecKeyDataStorePtr x509Store;
    PCCERT_CONTEXT pCert = NULL;
    int ret;

    xmlSecAssert2( mngr != NULL, -1 );
    xmlSecAssert2( data != NULL, -1 );
    xmlSecAssert2( dataSize > 0, -1 );
    xmlSecAssert2( format != xmlSecKeyDataFormatUnknown, -1 );

    x509Store = xmlSecKeysMngrGetDataStore( mngr, 
xmlSecMSCryptoX509StoreId );
    if( x509Store == NULL ) {
        xmlSecError( XMLSEC_ERRORS_HERE,
            "xmlSecMSCryptoAppKeysMngrCertLoadMemory",
            "xmlSecKeysMngrGetDataStore",
            XMLSEC_ERRORS_R_XMLSEC_FAILED,
            "xmlSecMSCryptoX509StoreId" );
        return -1;
    }

    CERT_BLOB cb;
    cb.pbData = (xmlSecByte*)data;
    cb.cbData = dataSize;

    DWORD dwExpectedFormatTypeFlags;

    switch( format ) {
        case xmlSecKeyDataFormatDer:
        case xmlSecKeyDataFormatCertDer:
            dwExpectedFormatTypeFlags = CERT_QUERY_FORMAT_FLAG_BINARY;
            break;
        case xmlSecKeyDataFormatPem:
            dwExpectedFormatTypeFlags = 
CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED;
            break;
        default:
            xmlSecError( XMLSEC_ERRORS_HERE, NULL, NULL, 
XMLSEC_ERRORS_R_INVALID_FORMAT, "format=%d", format );
            return -1;
    }
   
    if( !CryptQueryObject( CERT_QUERY_OBJECT_BLOB, &cb, 
CERT_QUERY_CONTENT_FLAG_CERT, dwExpectedFormatTypeFlags, 0, 0, 0, 0, 0, 
0, (const void**)&pCert ) ) {
        xmlSecError( XMLSEC_ERRORS_HERE,
            "xmlSecMSCryptoAppKeysMngrCertLoadMemory",
            "CryptQueryObject",
            XMLSEC_ERRORS_R_XMLSEC_FAILED,
            "error=0x%x", GetLastError() );
        return -1;
    }

    xmlSecAssert2( pCert != NULL, -1 );
    ret = xmlSecMSCryptoX509StoreAdoptCert( x509Store, pCert, type );
    if( ret < 0 ) {
        xmlSecError( XMLSEC_ERRORS_HERE,
            "xmlSecMSCryptoAppKeysMngrCertLoadMemory",
            "xmlSecMSCryptoX509StoreAdoptCert",
            XMLSEC_ERRORS_R_XMLSEC_FAILED,
            XMLSEC_ERRORS_NO_MESSAGE );
        CertFreeCertificateContext( pCert );
        return -1;
    }
   
    return 0;
}

static int xmlSecMSCryptoAppKeysMngrCertLoad( xmlSecKeysMngrPtr mngr, 
const char* filename, xmlSecKeyDataFormat format, xmlSecKeyDataType type )
{
    xmlSecKeyDataStorePtr x509Store;
    PCCERT_CONTEXT pCert = NULL;
    int ret;
   
    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(filename != NULL, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);

    x509Store = xmlSecKeysMngrGetDataStore( mngr, 
xmlSecMSCryptoX509StoreId );
    if( x509Store == NULL ) {
        xmlSecError( XMLSEC_ERRORS_HERE,
            "xmlSecMSCryptoAppKeysMngrCertLoadMemory",
            "xmlSecKeysMngrGetDataStore",
            XMLSEC_ERRORS_R_XMLSEC_FAILED,
            "xmlSecMSCryptoX509StoreId" );
        return -1;
    }

    DWORD dwExpectedFormatTypeFlags;

    switch( format ) {
        case xmlSecKeyDataFormatDer:
        case xmlSecKeyDataFormatCertDer:
            dwExpectedFormatTypeFlags = CERT_QUERY_FORMAT_FLAG_BINARY;
            break;
        case xmlSecKeyDataFormatPem:
            dwExpectedFormatTypeFlags = 
CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED;
            break;
        default:
            xmlSecError( XMLSEC_ERRORS_HERE, NULL, NULL, 
XMLSEC_ERRORS_R_INVALID_FORMAT, "format=%d", format );
            return -1;
    }

    QString qsFilename( filename );
    if( !CryptQueryObject( CERT_QUERY_OBJECT_FILE, qsFilename.ucs2(), 
CERT_QUERY_CONTENT_FLAG_CERT, dwExpectedFormatTypeFlags, 0, 0, 0, 0, 0, 
0, (const void**)&pCert ) ) {
        xmlSecError( XMLSEC_ERRORS_HERE,
            "xmlSecMSCryptoAppKeysMngrCertLoad",
            "CryptQueryObject",
            XMLSEC_ERRORS_R_XMLSEC_FAILED,
            "error=0x%x", GetLastError() );
        return -1;
    }

    xmlSecAssert2( pCert != NULL, -1 );
    ret = xmlSecMSCryptoX509StoreAdoptCert( x509Store, pCert, type );
    if( ret < 0 ) {
        xmlSecError( XMLSEC_ERRORS_HERE,
            "xmlSecMSCryptoAppKeysMngrCertLoad",
            "xmlSecMSCryptoX509StoreAdoptCert",
            XMLSEC_ERRORS_R_XMLSEC_FAILED,
            XMLSEC_ERRORS_NO_MESSAGE );
        CertFreeCertificateContext( pCert );
        return -1;
    }
   
    return 0;
}

Also I wrote function to create xmlSecKeyPtr from PCCERT_CONTEXT. I did 
not find anything like that in the xmlsec, but maybe I am missing something.
I find this very useful, because I already have PCCERT_CONTEXT in my 
application.

static xmlSecKeyPtr xmlSecMSCryptoAppKeyLoadContext( PCCERT_CONTEXT pCert )
{
    PCCERT_CONTEXT tmpcert = NULL;
    xmlSecKeyDataPtr x509Data = NULL;
    xmlSecKeyDataPtr keyData = NULL;
    xmlSecKeyPtr key = NULL;
    xmlSecKeyPtr res = NULL;
    int ret;

    x509Data = xmlSecKeyDataCreate( xmlSecMSCryptoKeyDataX509Id );
    if( x509Data == NULL ) {
        warning( "xmlSecMSCryptoAppKeyLoadContext(): failed in 
xmlSecKeyDataCreate" );
        goto done;
    }

    tmpcert = CertDuplicateCertificateContext( pCert );
    if( tmpcert == NULL ) {
        warning( "xmlSecMSCryptoAppKeyLoadContext(): failed in 
CertDuplicateCertificateContext" );
        goto done;
    }

    ret = xmlSecMSCryptoKeyDataX509AdoptKeyCert( x509Data, tmpcert );
    if( ret < 0 ) {
        warning( "xmlSecMSCryptoAppKeyLoadContext(): failed in 
xmlSecMSCryptoKeyDataX509AdoptKeyCert" );
        CertFreeCertificateContext( tmpcert );
        goto done;
    }
    tmpcert = NULL;

    keyData = xmlSecMSCryptoCertAdopt( pCert, xmlSecKeyDataTypePrivate | 
xmlSecKeyDataTypePublic );
    if( keyData == NULL ) {
        warning( "xmlSecMSCryptoAppKeyLoadContext(): failed in 
xmlSecMSCryptoCertAdopt" );
        goto done;
    }

    tmpcert = CertDuplicateCertificateContext( pCert );
    ret = xmlSecMSCryptoKeyDataX509AdoptCert( x509Data, tmpcert );
    if( ret < 0 ) {
        warning( "xmlSecMSCryptoAppKeyCertLoadContext(): falied in 
xmlSecMSCryptoKeyDataX509AdoptCert" );
        goto done;
    }

    key = xmlSecKeyCreate();
    if( key == NULL ) {
        warning( "xmlSecMSCryptoAppKeyLoadContext(): failed in 
xmlSecKeyCreate" );
        goto done;
    }   
   
    ret = xmlSecKeySetValue( key, keyData );
    if( ret < 0 ) {
        warning( "xmlSecMSCryptoAppKeyLoadContext(): failed in 
xmlSecKeySetValue" );
        goto done;
    }
    keyData = NULL;

    ret = xmlSecKeyAdoptData( key, x509Data );
    if( ret < 0 ) {
        warning( "xmlSecMSCryptoAppKeyLoadContext(): failed in 
xmlSecKeyAdoptData" );
        goto done;
    }
    x509Data = NULL;

    /* success */
    res = key;
    key = NULL;
done:
    if( tmpcert != NULL ) {
        CertFreeCertificateContext( tmpcert );
    }
    if( x509Data != NULL ) {
        xmlSecKeyDataDestroy( x509Data );
    }
    if( keyData != NULL ) {
        xmlSecKeyDataDestroy( keyData );
    }
    if( key != NULL ) {
        xmlSecKeyDestroy( key );
    }
    return( res );
}

And in the end I have question. I would like to set pin programatically, 
when user uses certificate on the smart card.
When I use PKCS7 signature I do that using this code:

#define PP_KEYEXCHANGE_PIN 32
#define PP_SIGNATURE_PIN 33

PCCERT_CONTEXT pSignerCert;
char* pin;

HCRYPTPROV hCryptProv = NULL;
BOOL fReturn = FALSE;

fReturn = CryptAcquireContextW( &hCryptProv, containerName, 
providerName, PROV_RSA_FULL, NULL );
if( !fReturn && GetLastError() == NTE_BAD_KEYSET ) {
    debug( "signDataMsCrypto(): CryptAcquireContext - NTE_BAD_KEYSET try 
machine keyset" );
    fReturn = CryptAcquireContextW( &hCryptProv, containerName, 
providerName, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET );
}
if( !fReturn ) {
    warning( "signDataMsCrypto(): CryptAcquireContext - 0x%x", 
GetLastError() );
    return SignFailed;
}
fReturn = FALSE;

// key specifier ( AT_KEYEXCHANGE = 1, AT_SIGNATURE = 2 )
unsigned long pcbData;
unsigned long keySpec;
if( !CertGetCertificateContextProperty( pCertContext, 
CERT_KEY_SPEC_PROP_ID, &keySpec, &pcbData ) ) {
    warning( "CertGetCertificateContextProperty(): CERT_KEY_SPEC_PROP_ID 
error 0x%x", GetLastError() );
}

// set pin!
if( pin ) {
    debug( "signDataMsCrypto(): set pin begin" );
    if( keySpec == AT_KEYEXCHANGE ) {
        if( !CryptSetProvParam( hCryptProv, PP_KEYEXCHANGE_PIN, 
(unsigned char*)pin, 0 ) ) {
            warning( "signDataMsCrypto(): 
CryptSetProvParam(PP_KEYEXCHANGE_PIN)  - 0x%x", GetLastError() );
            return SignFailed;
        }
        debug( "signDataMsCrypto(): exchange pin set ok" );
    }
    else if( keySpec == AT_SIGNATURE ) {
        if( !CryptSetProvParam( hCryptProv, PP_SIGNATURE_PIN, (unsigned 
char*)pin, 0 ) ) {
            warning( "signDataMsCrypto(): 
CryptSetProvParam(PP_SIGNATURE_PIN) - 0x%x", GetLastError() );
            return SignFailed;
        }
        qDebug( "signDataMsCrypto(): signature pin set ok" );
    }
    qDebug( "signDataMsCrypto(): set pin end" );
}

Pin could be passed through xmlSecDSigCtx structure. I just do not know 
where I should apply this in xmlsec.
Please tell me where the actual signing is going on so I can add this 
pin setting. I thing that would also be useful adding to mscrypto in xmlsec.

Thanks for your help.

Best regards,
Igor Odar

-- 
___________________________________________
Igor Odar
EBA, Agencija za elektronsko poslovanje d.o.o.
Teslova ulica 30
SI-1000 Ljubljana
e-mail: igor.odar at eba.si
internet: www.eba.si
GSM: +386 (0)40 461 194
TEL: +386 (0)1 477 66 61
___________________________________________




More information about the xmlsec mailing list