From 1bcd4edbcc3abae7d39b7da2d79fce6eef5934d5 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 14 May 2018 21:33:42 +0200 Subject: [PATCH] MSCNG: add keys store support (#173) * mscng: add keysstore support This also requires changes in the x509 key data class. It's no longer safe to call CertDeleteCertificateFromStore() for no longer needed certificates, as the store may be a non-in-memory one, and the user expects that the system store is only read by xmlsec. Non-deleting also means that we can't assert anymore that the in-memory cert store is empty when we delete it. We don't leak memory with this, as the refcount of the certificate contexts is still properly decremented. As a side effect 6 TODOs added earlier are now resolved. * mscng: add initial documentation Also fix the inconsistency that email address is sometimes stated, sometimes not. Now it's omitted everywhere and the AUTHORS file has it at a single place. * mscng: add keysstore test A manual test is not ideal, but it's better than nothing. --- AUTHORS | 1 + include/xmlsec/mscng/app.h | 3 +- include/xmlsec/mscng/certkeys.h | 2 +- include/xmlsec/mscng/crypto.h | 2 +- include/xmlsec/mscng/keysstore.h | 41 +++ include/xmlsec/mscng/symbols.h | 2 +- include/xmlsec/mscng/x509.h | 4 +- src/mscng/README | 16 + src/mscng/app.c | 86 ++++-- src/mscng/certkeys.c | 2 +- src/mscng/crypto.c | 2 +- src/mscng/digests.c | 2 +- src/mscng/globals.h | 2 +- src/mscng/keysstore.c | 510 +++++++++++++++++++++++++++++++ src/mscng/signatures.c | 2 +- src/mscng/x509.c | 8 +- src/mscng/x509vfy.c | 33 +- tests/keysstore/README | 9 + tests/keysstore/keysstore.xml | 17 ++ win32/Makefile.msvc | 2 + 20 files changed, 696 insertions(+), 50 deletions(-) create mode 100644 include/xmlsec/mscng/keysstore.h create mode 100644 src/mscng/README create mode 100644 src/mscng/keysstore.c create mode 100644 tests/keysstore/README create mode 100644 tests/keysstore/keysstore.xml diff --git a/AUTHORS b/AUTHORS index 3003d13ea..ec4340299 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,5 +4,6 @@ Windows port: Igor Zlatkovic Debian port: John Belmonte xmlsec-nss: Tej Arora , AOL Inc. xmlsec-mscrypto: Wouter Ketting , Cordys R&D BV +xmlsec-mscng: Miklos Vajna GOST support: Dmitry Belyavsky , Cryptocom LTD (http://www.cryptocom.ru) diff --git a/include/xmlsec/mscng/app.h b/include/xmlsec/mscng/app.h index 3eaaa7993..33b40e42f 100644 --- a/include/xmlsec/mscng/app.h +++ b/include/xmlsec/mscng/app.h @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #ifndef __XMLSEC_MSCNG_APP_H__ #define __XMLSEC_MSCNG_APP_H__ @@ -25,6 +25,7 @@ extern "C" { ********************************************************************/ XMLSEC_CRYPTO_EXPORT int xmlSecMSCngAppInit (const char* config); XMLSEC_CRYPTO_EXPORT int xmlSecMSCngAppShutdown (void); +XMLSEC_CRYPTO_EXPORT LPCTSTR xmlSecMSCngAppGetCertStoreName (void); /******************************************************************** * diff --git a/include/xmlsec/mscng/certkeys.h b/include/xmlsec/mscng/certkeys.h index 6ef284d15..ad7edb9ed 100644 --- a/include/xmlsec/mscng/certkeys.h +++ b/include/xmlsec/mscng/certkeys.h @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #ifndef __XMLSEC_MSCNG_CERTKEYS_H__ #define __XMLSEC_MSCNG_CERTKEYS_H__ diff --git a/include/xmlsec/mscng/crypto.h b/include/xmlsec/mscng/crypto.h index cd1650acf..3eb14da51 100644 --- a/include/xmlsec/mscng/crypto.h +++ b/include/xmlsec/mscng/crypto.h @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #ifndef __XMLSEC_MSCNG_CRYPTO_H__ #define __XMLSEC_MSCNG_CRYPTO_H__ diff --git a/include/xmlsec/mscng/keysstore.h b/include/xmlsec/mscng/keysstore.h new file mode 100644 index 000000000..bd8b01705 --- /dev/null +++ b/include/xmlsec/mscng/keysstore.h @@ -0,0 +1,41 @@ +/* + * XML Security Library (http://www.aleksey.com/xmlsec). + * + * This is free software; see Copyright file in the source + * distribution for preciese wording. + * + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. + */ +#ifndef __XMLSEC_MSCNG_KEYSSTORE_H__ +#define __XMLSEC_MSCNG_KEYSSTORE_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +/** + * xmlSecMSCngKeysStoreId: + * + * A MSCng keys store klass id. + */ +#define xmlSecMSCngKeysStoreId xmlSecMSCngKeysStoreGetKlass() + +XMLSEC_CRYPTO_EXPORT xmlSecKeyStoreId xmlSecMSCngKeysStoreGetKlass(void); +XMLSEC_CRYPTO_EXPORT int xmlSecMSCngKeysStoreAdoptKey(xmlSecKeyStorePtr store, + xmlSecKeyPtr key); +XMLSEC_CRYPTO_EXPORT int xmlSecMSCngKeysStoreLoad (xmlSecKeyStorePtr store, + const char *uri, + xmlSecKeysMngrPtr keysMngr); +XMLSEC_CRYPTO_EXPORT int xmlSecMSCngKeysStoreSave (xmlSecKeyStorePtr store, + const char *filename, + xmlSecKeyDataType type); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __XMLSEC_MSCNG_PCCERT_CONTEXT_H__ */ + + diff --git a/include/xmlsec/mscng/symbols.h b/include/xmlsec/mscng/symbols.h index c8d5e0731..6de777f37 100644 --- a/include/xmlsec/mscng/symbols.h +++ b/include/xmlsec/mscng/symbols.h @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #ifndef __XMLSEC_MSCNG_SYMBOLS_H__ #define __XMLSEC_MSCNG_SYMBOLS_H__ diff --git a/include/xmlsec/mscng/x509.h b/include/xmlsec/mscng/x509.h index ebbf8758b..3f49dfe52 100644 --- a/include/xmlsec/mscng/x509.h +++ b/include/xmlsec/mscng/x509.h @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #ifndef __XMLSEC_MSCNG_X509_H__ #define __XMLSEC_MSCNG_X509_H__ @@ -55,6 +55,8 @@ XMLSEC_CRYPTO_EXPORT int xmlSecMSCngKeyDataX509AdoptCert (xm XMLSEC_CRYPTO_EXPORT int xmlSecMSCngX509StoreAdoptCert (xmlSecKeyDataStorePtr store, PCCERT_CONTEXT cert, xmlSecKeyDataType type); +XMLSEC_CRYPTO_EXPORT int xmlSecMSCngX509StoreAdoptKeyStore (xmlSecKeyDataStorePtr store, + HCERTSTORE keyStore); XMLSEC_CRYPTO_EXPORT PCCERT_CONTEXT xmlSecMSCngX509StoreVerify (xmlSecKeyDataStorePtr store, HCERTSTORE certs, xmlSecKeyInfoCtx* keyInfoCtx); diff --git a/src/mscng/README b/src/mscng/README new file mode 100644 index 000000000..72b37baf5 --- /dev/null +++ b/src/mscng/README @@ -0,0 +1,16 @@ +What version of Windows? +------------------------------------------------------------------------ + +The Microsoft CNG API is a set of BCrypt* and NCrypt* functions. Taking +BCryptOpenAlgorithmProvider() as a representative example, the minimum +supported client is Windows Vista and the minimum supported server is Windows +Server 2008. + +Keys manager with MS Certificate store support. +------------------------------------------------------------------------ + +Similarly to the nss and mscrypto backends, the xmlsec-mscng keys manager is +based on the simple keys store from xmlsec core. If keys are not found in the +simple keys store, then the MS Certificate store (the "MY" store by default, +visible as Personal -> Certificates in certmgr.msc) is used to look up keys. +The certificate store from the OS is a read-only store. diff --git a/src/mscng/app.c b/src/mscng/app.c index 999ee383f..4c19def89 100644 --- a/src/mscng/app.c +++ b/src/mscng/app.c @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #include "globals.h" @@ -23,6 +23,10 @@ #include #include #include +#include + +/* config info for the mscng keysstore */ +static LPTSTR gXmlSecMSCngAppCertStoreName = NULL; /** * xmlSecMSCngAppInit: @@ -36,8 +40,26 @@ */ int xmlSecMSCngAppInit(const char* config) { - UNREFERENCED_PARAMETER(config); - /* TODO: initialize MSCng crypto engine */ + /* initialize MSCng crypto engine */ + + /* config parameter is an ms cert store name */ + if(config != NULL && strlen(config) > 0) { + if(gXmlSecMSCngAppCertStoreName != NULL) { + /* deny double initialization */ + xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_CONFIG, NULL, + "config=%s, config already set", + xmlSecErrorsSafeString(config)); + return(-1); + } + + gXmlSecMSCngAppCertStoreName = xmlSecMSCngConvertUtf8ToUnicode((const xmlChar*)config); + if(gXmlSecMSCngAppCertStoreName == NULL) { + xmlSecInternalError2("xmlSecMSCngConvertUtf8ToUnicode", NULL, + "config=%s", xmlSecErrorsSafeString(config)); + return(-1); + } + } + return(0); } @@ -52,11 +74,26 @@ xmlSecMSCngAppInit(const char* config) { */ int xmlSecMSCngAppShutdown(void) { - /* TODO: shutdown MSCng crypto engine */ - + /* shutdown MSCng crypto engine */ + if(gXmlSecMSCngAppCertStoreName != NULL) { + xmlFree(gXmlSecMSCngAppCertStoreName); + gXmlSecMSCngAppCertStoreName = NULL; + } return(0); } +/** + * xmlSecMSCngAppGetCertStoreName: + * + * Gets the MS Cng certs store name set by @xmlSecMSCngAppInit function. + * + * Returns: the MS Cng certs name used by xmlsec-mscng. + */ +LPCTSTR +xmlSecMSCngAppGetCertStoreName(void) { + return(gXmlSecMSCngAppCertStoreName); +} + /** * xmlSecMSCngAppKeyLoad: * @filename: the key filename. @@ -524,17 +561,13 @@ xmlSecMSCngAppDefaultKeysMngrInit(xmlSecKeysMngrPtr mngr) { xmlSecAssert2(mngr != NULL, -1); - /* TODO: if MSCng crypto engine has another default - * keys storage then use it! - */ - - /* create simple keys store if needed */ + /* create MSCng keys store if needed */ if(xmlSecKeysMngrGetKeysStore(mngr) == NULL) { xmlSecKeyStorePtr keysStore; - keysStore = xmlSecKeyStoreCreate(xmlSecSimpleKeysStoreId); + keysStore = xmlSecKeyStoreCreate(xmlSecMSCngKeysStoreId); if(keysStore == NULL) { - xmlSecInternalError("xmlSecKeyStoreCreate(xmlSecSimpleKeysStoreId)", NULL); + xmlSecInternalError("xmlSecKeyStoreCreate(xmlSecMSCngKeysStoreId)", NULL); return(-1); } @@ -574,19 +607,15 @@ xmlSecMSCngAppDefaultKeysMngrAdoptKey(xmlSecKeysMngrPtr mngr, xmlSecKeyPtr key) xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(key != NULL, -1); - /* TODO: if MSCng crypto engine has another default - * keys storage then use it! - */ - store = xmlSecKeysMngrGetKeysStore(mngr); if(store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL); return(-1); } - ret = xmlSecSimpleKeysStoreAdoptKey(store, key); + ret = xmlSecMSCngKeysStoreAdoptKey(store, key); if(ret < 0) { - xmlSecInternalError("xmlSecSimpleKeysStoreAdoptKey", NULL); + xmlSecInternalError("xmlSecMSCngKeysStoreAdoptKey", NULL); return(-1); } @@ -611,20 +640,16 @@ xmlSecMSCngAppDefaultKeysMngrLoad(xmlSecKeysMngrPtr mngr, const char* uri) { xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(uri != NULL, -1); - /* TODO: if MSCng crypto engine has another default - * keys storage then use it! - */ - store = xmlSecKeysMngrGetKeysStore(mngr); if(store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL); return(-1); } - ret = xmlSecSimpleKeysStoreLoad(store, uri, mngr); + ret = xmlSecMSCngKeysStoreLoad(store, uri, mngr); if(ret < 0) { - xmlSecInternalError2("xmlSecSimpleKeysStoreLoad", NULL, - "uri=%s", xmlSecErrorsSafeString(uri)); + xmlSecInternalError2("xmlSecMSCngKeysStoreLoad", NULL, "uri=%s", + xmlSecErrorsSafeString(uri)); return(-1); } @@ -649,21 +674,16 @@ xmlSecMSCngAppDefaultKeysMngrSave(xmlSecKeysMngrPtr mngr, const char* filename, xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(filename != NULL, -1); - /* TODO: if MSCng crypto engine has another default - * keys storage then use it! - */ - store = xmlSecKeysMngrGetKeysStore(mngr); if(store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL); return(-1); } - ret = xmlSecSimpleKeysStoreSave(store, filename, type); + ret = xmlSecMSCngKeysStoreSave(store, filename, type); if(ret < 0) { - xmlSecInternalError2("xmlSecSimpleKeysStoreSave", NULL, - "filename=%s", - xmlSecErrorsSafeString(filename)); + xmlSecInternalError2("xmlSecMSCngKeysStoreSave", NULL, "filename%s", + xmlSecErrorsSafeString(filename)); return(-1); } diff --git a/src/mscng/certkeys.c b/src/mscng/certkeys.c index 1de3e95a9..bc3206cb0 100644 --- a/src/mscng/certkeys.c +++ b/src/mscng/certkeys.c @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #include "globals.h" diff --git a/src/mscng/crypto.c b/src/mscng/crypto.c index ef67f2657..24da1a3ee 100644 --- a/src/mscng/crypto.c +++ b/src/mscng/crypto.c @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #include "globals.h" diff --git a/src/mscng/digests.c b/src/mscng/digests.c index 36b7c552e..8acd6c9ae 100644 --- a/src/mscng/digests.c +++ b/src/mscng/digests.c @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #include "globals.h" diff --git a/src/mscng/globals.h b/src/mscng/globals.h index d1adc9698..24becae06 100644 --- a/src/mscng/globals.h +++ b/src/mscng/globals.h @@ -6,7 +6,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #ifndef __XMLSEC_GLOBALS_H__ #define __XMLSEC_GLOBALS_H__ diff --git a/src/mscng/keysstore.c b/src/mscng/keysstore.c new file mode 100644 index 000000000..2abbebac0 --- /dev/null +++ b/src/mscng/keysstore.c @@ -0,0 +1,510 @@ +/* + * XML Security Library (http://www.aleksey.com/xmlsec). + * + * This is free software; see Copyright file in the source + * distribution for preciese wording. + * + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. + */ +#include "globals.h" + +#include + +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define XMLSEC_MSCNG_APP_DEFAULT_CERT_STORE_NAME L"MY" + +/**************************************************************************** + * + * MSCng Keys Store. Uses Simple Keys Store under the hood + * + * Simple Keys Store ptr is located after xmlSecKeyStore + * + ***************************************************************************/ +#define xmlSecMSCngKeysStoreSize (sizeof(xmlSecKeyStore) + sizeof(xmlSecKeyStorePtr)) + +#define xmlSecMSCngKeysStoreGetSS(store) \ + ((xmlSecKeyStoreCheckSize((store), xmlSecMSCngKeysStoreSize)) ? \ + (xmlSecKeyStorePtr*)(((xmlSecByte*)(store)) + sizeof(xmlSecKeyStore)) : \ + (xmlSecKeyStorePtr*)NULL) + +static int +xmlSecMSCngKeysStoreInitialize(xmlSecKeyStorePtr store) { + xmlSecKeyStorePtr *ss; + + xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCngKeysStoreId), -1); + + ss = xmlSecMSCngKeysStoreGetSS(store); + xmlSecAssert2(*ss == NULL, -1); + + *ss = xmlSecKeyStoreCreate(xmlSecSimpleKeysStoreId); + if(*ss == NULL) { + xmlSecInternalError("xmlSecKeyStoreCreate", + xmlSecKeyStoreGetName(store)); + return(-1); + } + + return(0); +} + +static void +xmlSecMSCngKeysStoreFinalize(xmlSecKeyStorePtr store) { + xmlSecKeyStorePtr *ss; + + xmlSecAssert(xmlSecKeyStoreCheckId(store, xmlSecMSCngKeysStoreId)); + + ss = xmlSecMSCngKeysStoreGetSS(store); + xmlSecAssert((ss != NULL) && (*ss != NULL)); + + xmlSecKeyStoreDestroy(*ss); +} + +static PCCERT_CONTEXT +xmlSecMSCngKeysStoreFindCert(xmlSecKeyStorePtr store, const xmlChar* name, + xmlSecKeyInfoCtxPtr keyInfoCtx) { + LPCTSTR storeName; + HCERTSTORE hStore = NULL; + PCCERT_CONTEXT pCertContext = NULL; + LPTSTR wcName = NULL; + BOOL ret; + + xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCngKeysStoreId), NULL); + xmlSecAssert2(name != NULL, NULL); + xmlSecAssert2(keyInfoCtx != NULL, NULL); + + storeName = xmlSecMSCngAppGetCertStoreName(); + if(storeName == NULL) { + storeName = XMLSEC_MSCNG_APP_DEFAULT_CERT_STORE_NAME; + } + + hStore = CertOpenSystemStore(0, storeName); + if(hStore == NULL) { + xmlSecMSCngLastError("CertOpenSystemStore", + xmlSecKeyStoreGetName(store)); + return(NULL); + } + + /* convert name to unicode */ + wcName = xmlSecMSCngConvertUtf8ToTstr(name); + if(wcName == NULL) { + xmlSecInternalError("xmlSecMSCngConvertUtf8ToTstr(name)", + xmlSecKeyStoreGetName(store)); + CertCloseStore(hStore, 0); + return(NULL); + } + + /* find cert based on subject */ + pCertContext = xmlSecMSCngX509FindCertBySubject( + hStore, + wcName, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); + + if(pCertContext == NULL) { + /* find cert based on friendly name */ + DWORD dwPropSize; + PBYTE pbFriendlyName; + PCCERT_CONTEXT pCertCtxIter = NULL; + + + while (1) { + pCertCtxIter = CertEnumCertificatesInStore(hStore, pCertCtxIter); + if(pCertCtxIter == NULL) { + break; + } + + ret = CertGetCertificateContextProperty(pCertCtxIter, + CERT_FRIENDLY_NAME_PROP_ID, + NULL, &dwPropSize); + if(ret != TRUE) { + continue; + } + + pbFriendlyName = xmlMalloc(dwPropSize); + if(pbFriendlyName == NULL) { + xmlSecMallocError(dwPropSize, xmlSecKeyStoreGetName(store)); + xmlFree(wcName); + CertCloseStore(hStore, 0); + return(NULL); + } + + ret = CertGetCertificateContextProperty(pCertCtxIter, + CERT_FRIENDLY_NAME_PROP_ID, + pbFriendlyName, + &dwPropSize); + if(ret != TRUE) { + xmlFree(pbFriendlyName); + continue; + } + + if(lstrcmp(wcName, (LPCTSTR)pbFriendlyName) == 0) { + pCertContext = pCertCtxIter; + xmlFree(pbFriendlyName); + break; + } + + xmlFree(pbFriendlyName); + } + } + + if(pCertContext == NULL) { + /* find cert based on part of the name */ + pCertContext = CertFindCertificateInStore( + hStore, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_SUBJECT_STR, + wcName, + NULL); + } + + + xmlFree(wcName); + /* dwFlags=0 means close the store with memory remaining allocated for + * contexts that have not been freed */ + CertCloseStore(hStore, 0); + + return(pCertContext); +} + +static xmlSecKeyPtr +xmlSecMSCngKeysStoreFindKey(xmlSecKeyStorePtr store, const xmlChar* name, + xmlSecKeyInfoCtxPtr keyInfoCtx) { + xmlSecKeyStorePtr* ss; + xmlSecKeyPtr key = NULL; + xmlSecKeyReqPtr keyReq = NULL; + PCCERT_CONTEXT pCertContext = NULL; + PCCERT_CONTEXT pDuplicatedCertContext = NULL; + xmlSecKeyDataPtr data = NULL; + xmlSecKeyDataPtr x509Data = NULL; + xmlSecKeyPtr res = NULL; + int ret; + + xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCngKeysStoreId), NULL); + xmlSecAssert2(keyInfoCtx != NULL, NULL); + + ss = xmlSecMSCngKeysStoreGetSS(store); + xmlSecAssert2(((ss != NULL) && (*ss != NULL)), NULL); + + /* look for the key in the simple store */ + key = xmlSecKeyStoreFindKey(*ss, name, keyInfoCtx); + if(key != NULL) { + return(key); + } + + /* look for a named public or private key in the OS store */ + if(name == NULL) { + goto done; + } + + keyReq = &(keyInfoCtx->keyReq); + if(!(keyReq->keyType & (xmlSecKeyDataTypePublic | xmlSecKeyDataTypePrivate))) { + goto done; + } + + pCertContext = xmlSecMSCngKeysStoreFindCert(store, name, keyInfoCtx); + if(pCertContext == NULL) { + goto done; + } + + /* set cert in x509 data */ + x509Data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataX509Id); + if(x509Data == NULL) { + xmlSecInternalError("xmlSecKeyDataCreate", + xmlSecKeyDataGetName(x509Data)); + goto done; + } + + pDuplicatedCertContext = CertDuplicateCertificateContext(pCertContext); + if(pDuplicatedCertContext == NULL) { + xmlSecMSCngLastError("CertDuplicateCertificateContext", + xmlSecKeyDataGetName(x509Data)); + goto done; + } + + ret = xmlSecMSCngKeyDataX509AdoptCert(x509Data, pDuplicatedCertContext); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeyDataX509AdoptCert", + xmlSecKeyDataGetName(x509Data)); + goto done; + } + pDuplicatedCertContext = NULL; + + pDuplicatedCertContext = CertDuplicateCertificateContext(pCertContext); + if(pDuplicatedCertContext == NULL) { + xmlSecMSCngLastError("CertDuplicateCertificateContext", + xmlSecKeyDataGetName(x509Data)); + goto done; + } + + ret = xmlSecMSCngKeyDataX509AdoptKeyCert(x509Data, pDuplicatedCertContext); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeyDataX509AdoptKeyCert", + xmlSecKeyDataGetName(x509Data)); + goto done; + } + pDuplicatedCertContext = NULL; + + /* set cert in key data */ + data = xmlSecMSCngCertAdopt(pCertContext, keyReq->keyType); + if(data == NULL) { + xmlSecInternalError("xmlSecMSCngCertAdopt", NULL); + goto done; + } + pCertContext = NULL; + + /* create key and add key data and x509 data to it */ + key = xmlSecKeyCreate(); + if(key == NULL) { + xmlSecInternalError("xmlSecKeyCreate", NULL); + goto done; + } + + ret = xmlSecKeySetValue(key, data); + if(ret < 0) { + xmlSecInternalError("xmlSecKeySetValue", xmlSecKeyDataGetName(data)); + goto done; + } + data = NULL; + + ret = xmlSecKeyAdoptData(key, x509Data); + if(ret < 0) { + xmlSecInternalError("xmlSecKeyAdoptData", + xmlSecKeyDataGetName(x509Data)); + goto done; + } + x509Data = NULL; + + /* set the name of the key to the given name */ + ret = xmlSecKeySetName(key, name); + if(ret < 0) { + xmlSecInternalError("xmlSecKeySetName", xmlSecKeyStoreGetName(store)); + goto done; + } + + /* now that we have a key, make sure it is valid */ + if(xmlSecKeyIsValid(key)) { + res = key; + key = NULL; + } + +done: + if(pCertContext != NULL) { + CertFreeCertificateContext(pCertContext); + } + + if(pDuplicatedCertContext != NULL) { + CertFreeCertificateContext(pDuplicatedCertContext); + } + + if(data != NULL) { + xmlSecKeyDataDestroy(data); + } + + if(x509Data != NULL) { + xmlSecKeyDataDestroy(x509Data); + } + + if(key != NULL) { + xmlSecKeyDestroy(key); + } + + return(res); +} + +static xmlSecKeyStoreKlass xmlSecMSCngKeysStoreKlass = { + sizeof(xmlSecKeyStoreKlass), + xmlSecMSCngKeysStoreSize, + + /* data */ + BAD_CAST "MSCng-keys-store", /* const xmlChar* name; */ + + /* constructors/destructor */ + xmlSecMSCngKeysStoreInitialize, /* xmlSecKeyStoreInitializeMethod initialize; */ + xmlSecMSCngKeysStoreFinalize, /* xmlSecKeyStoreFinalizeMethod finalize; */ + xmlSecMSCngKeysStoreFindKey, /* xmlSecKeyStoreFindKeyMethod findKey; */ + + /* reserved for the future */ + NULL, /* void* reserved0; */ + NULL, /* void* reserved1; */ +}; + +/** + * xmlSecMSCngKeysStoreGetKlass: + * + * The MSCng list based keys store klass. + * + * Returns: MSCng list based keys store klass. + */ +xmlSecKeyStoreId +xmlSecMSCngKeysStoreGetKlass(void) { + return(&xmlSecMSCngKeysStoreKlass); +} + +/** + * xmlSecMSCngKeysStoreAdoptKey: + * @store: the pointer to MSCng keys store. + * @key: the pointer to key. + * + * Adds @key to the @store. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngKeysStoreAdoptKey(xmlSecKeyStorePtr store, xmlSecKeyPtr key) { + xmlSecKeyStorePtr *ss; + + xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCngKeysStoreId), -1); + xmlSecAssert2((key != NULL), -1); + + ss = xmlSecMSCngKeysStoreGetSS(store); + xmlSecAssert2(ss != NULL, -1); + xmlSecAssert2(*ss != NULL, -1); + xmlSecAssert2(xmlSecKeyStoreCheckId(*ss, xmlSecSimpleKeysStoreId), -1); + + return(xmlSecSimpleKeysStoreAdoptKey(*ss, key)); +} + +/** + * xmlSecMSCngKeysStoreLoad: + * @store: the pointer to MSCng keys store. + * @uri: the filename. + * @keysMngr: the pointer to associated keys manager. + * + * Reads keys from an XML file. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngKeysStoreLoad(xmlSecKeyStorePtr store, const char *uri, + xmlSecKeysMngrPtr keysMngr) { + xmlDocPtr doc; + xmlNodePtr root; + xmlNodePtr cur; + xmlSecKeyPtr key; + xmlSecKeyInfoCtx keyInfoCtx; + int ret; + + xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCngKeysStoreId), -1); + xmlSecAssert2((uri != NULL), -1); + + doc = xmlParseFile(uri); + if(doc == NULL) { + xmlSecXmlError2("xmlParseFile", xmlSecKeyStoreGetName(store), "uri=%s", + xmlSecErrorsSafeString(uri)); + return(-1); + } + + root = xmlDocGetRootElement(doc); + if(!xmlSecCheckNodeName(root, BAD_CAST "Keys", xmlSecNs)) { + xmlSecInvalidNodeError(root, BAD_CAST "Keys", xmlSecKeyStoreGetName(store)); + xmlFreeDoc(doc); + return(-1); + } + + cur = xmlSecGetNextElementNode(root->children); + while((cur != NULL) && xmlSecCheckNodeName(cur, xmlSecNodeKeyInfo, xmlSecDSigNs)) { + key = xmlSecKeyCreate(); + if(key == NULL) { + xmlSecInternalError("xmlSecKeyCreate", + xmlSecKeyStoreGetName(store)); + xmlFreeDoc(doc); + return(-1); + } + + ret = xmlSecKeyInfoCtxInitialize(&keyInfoCtx, NULL); + if(ret < 0) { + xmlSecInternalError("xmlSecKeyInfoCtxInitialize", + xmlSecKeyStoreGetName(store)); + xmlSecKeyDestroy(key); + xmlFreeDoc(doc); + return(-1); + } + + keyInfoCtx.mode = xmlSecKeyInfoModeRead; + keyInfoCtx.keysMngr = keysMngr; + keyInfoCtx.flags = XMLSEC_KEYINFO_FLAGS_DONT_STOP_ON_KEY_FOUND | + XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS; + keyInfoCtx.keyReq.keyId = xmlSecKeyDataIdUnknown; + keyInfoCtx.keyReq.keyType = xmlSecKeyDataTypeAny; + keyInfoCtx.keyReq.keyUsage= xmlSecKeyDataUsageAny; + + ret = xmlSecKeyInfoNodeRead(cur, key, &keyInfoCtx); + if(ret < 0) { + xmlSecInternalError("xmlSecKeyInfoNodeRead", + xmlSecKeyStoreGetName(store)); + xmlSecKeyInfoCtxFinalize(&keyInfoCtx); + xmlSecKeyDestroy(key); + xmlFreeDoc(doc); + return(-1); + } + xmlSecKeyInfoCtxFinalize(&keyInfoCtx); + + if(xmlSecKeyIsValid(key)) { + ret = xmlSecMSCngKeysStoreAdoptKey(store, key); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeysStoreAdoptKey", + xmlSecKeyStoreGetName(store)); + xmlSecKeyDestroy(key); + xmlFreeDoc(doc); + return(-1); + } + } else { + /* we have an unknown key in our file, just ignore it */ + xmlSecKeyDestroy(key); + } + cur = xmlSecGetNextElementNode(cur->next); + } + + if(cur != NULL) { + xmlSecUnexpectedNodeError(cur, xmlSecKeyStoreGetName(store)); + xmlFreeDoc(doc); + return(-1); + } + + xmlFreeDoc(doc); + return(0); +} + +/** + * xmlSecMSCngKeysStoreSave: + * @store: the pointer to MSCng keys store. + * @filename: the filename. + * @type: the saved keys type (public, private, ...). + * + * Writes keys from @store to an XML file. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngKeysStoreSave(xmlSecKeyStorePtr store, const char *filename, xmlSecKeyDataType type) { + xmlSecKeyStorePtr *ss; + + xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCngKeysStoreId), -1); + xmlSecAssert2((filename != NULL), -1); + + ss = xmlSecMSCngKeysStoreGetSS(store); + xmlSecAssert2(ss != NULL, -1); + xmlSecAssert2(*ss != NULL, -1); + xmlSecAssert2(xmlSecKeyStoreCheckId(*ss, xmlSecSimpleKeysStoreId), -1); + + return(xmlSecSimpleKeysStoreSave(*ss, filename, type)); +} diff --git a/src/mscng/signatures.c b/src/mscng/signatures.c index 5382bdfb3..3ae01df2e 100644 --- a/src/mscng/signatures.c +++ b/src/mscng/signatures.c @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #include "globals.h" diff --git a/src/mscng/x509.c b/src/mscng/x509.c index 89d2593c8..c0b24528a 100644 --- a/src/mscng/x509.c +++ b/src/mscng/x509.c @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #include "globals.h" @@ -124,17 +124,13 @@ xmlSecMSCngKeyDataX509Finalize(xmlSecKeyDataPtr data) { xmlSecAssert(ctx != NULL); if(ctx->cert != NULL) { - if(!CertDeleteCertificateFromStore(ctx->cert)) { - xmlSecMSCngLastError("CertDeleteCertificateFromStore", NULL); - } - if(!CertFreeCertificateContext(ctx->cert)) { xmlSecMSCngLastError("CertFreeCertificateContext", NULL); } } if(ctx->hMemStore != 0) { - if(!CertCloseStore(ctx->hMemStore, CERT_CLOSE_STORE_CHECK_FLAG)) { + if(!CertCloseStore(ctx->hMemStore, 0)) { xmlSecMSCngLastError("CertCloseStore", NULL); } } diff --git a/src/mscng/x509vfy.c b/src/mscng/x509vfy.c index caad349e1..270d68955 100644 --- a/src/mscng/x509vfy.c +++ b/src/mscng/x509vfy.c @@ -4,7 +4,7 @@ * This is free software; see Copyright file in the source * distribution for preciese wording. * - * Copyright (C) 2018 Miklos Vajna . All Rights Reserved. + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ #include "globals.h" @@ -82,6 +82,37 @@ xmlSecMSCngX509StoreFinalize(xmlSecKeyDataStorePtr store) { memset(ctx, 0, sizeof(xmlSecMSCngX509StoreCtx)); } +/** + * xmlSecMSCngX509StoreAdoptKeyStore: + * @store: the pointer to X509 key data store klass. + * @keyStore: the pointer to keys store. + * + * Adds @keyStore to the list of key stores. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngX509StoreAdoptKeyStore(xmlSecKeyDataStorePtr store, HCERTSTORE keyStore) { + xmlSecMSCngX509StoreCtxPtr ctx; + int ret; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); + xmlSecAssert2(keyStore != NULL, -1); + + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, -1); + xmlSecAssert2(ctx->trusted != NULL, -1); + + ret = CertAddStoreToCollection(ctx->trusted, keyStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 2); + if(ret != TRUE) { + xmlSecMSCngLastError("CertAddStoreToCollection", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + return(0); +} + static int xmlSecMSCngX509StoreInitialize(xmlSecKeyDataStorePtr store) { int ret; diff --git a/tests/keysstore/README b/tests/keysstore/README new file mode 100644 index 000000000..a14c308bc --- /dev/null +++ b/tests/keysstore/README @@ -0,0 +1,9 @@ +Manual test of the keysstore feature +---------------------------------------------- + +mscng backend: + +- import tests/keys/rsakey-win.p12 (double-click on it) +- verify that the import happened, using certmgr.msc +- sign: win32/binaries/xmlsec.exe sign --crypto mscng --output out.xml tests/keysstore/keysstore.xml +- verify: win32/binaries/xmlsec.exe verify --crypto mscng out.xml diff --git a/tests/keysstore/keysstore.xml b/tests/keysstore/keysstore.xml new file mode 100644 index 000000000..933ab3fcc --- /dev/null +++ b/tests/keysstore/keysstore.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + TestRsaKey + + some text + diff --git a/win32/Makefile.msvc b/win32/Makefile.msvc index 6023bd354..bdbbc3222 100644 --- a/win32/Makefile.msvc +++ b/win32/Makefile.msvc @@ -318,6 +318,7 @@ XMLSEC_MSCNG_OBJS = \ $(XMLSEC_MSCNG_INTDIR)\kw_des.obj \ $(XMLSEC_MSCNG_INTDIR)\strings.obj\ $(XMLSEC_MSCNG_INTDIR)\signatures.obj\ + $(XMLSEC_MSCNG_INTDIR)\keysstore.obj \ $(XMLSEC_MSCNG_INTDIR)\x509.obj\ $(XMLSEC_MSCNG_INTDIR)\x509vfy.obj XMLSEC_MSCNG_OBJS_A = \ @@ -333,6 +334,7 @@ XMLSEC_MSCNG_OBJS_A = \ $(XMLSEC_MSCNG_INTDIR_A)\kw_des.obj \ $(XMLSEC_MSCNG_INTDIR_A)\strings.obj\ $(XMLSEC_MSCNG_INTDIR_A)\signatures.obj\ + $(XMLSEC_MSCNG_INTDIR_A)\keysstore.obj \ $(XMLSEC_MSCNG_INTDIR_A)\x509.obj\ $(XMLSEC_MSCNG_INTDIR_A)\x509vfy.obj