[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