// /***************************************************************************** * * File: Sign.cpp * * Description: * This sample is a simple application that might be used as a starting-point * for an application that uses the Packaging API. This sample demonstrates * signature generation and validation using a sample signing policy described * in Sign.h * * ------------------------------------ * * This file is part of the Microsoft Windows SDK Code Samples. * * Copyright (C) Microsoft Corporation. All rights reserved. * * This source code is intended only as a supplement to Microsoft * Development Tools and/or on-line documentation. See these other * materials for detailed information regarding Microsoft code samples. * * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A * PARTICULAR PURPOSE. * ****************************************************************************/ #include #include #include #include // For OPC APIs #include // For certificate stuff #include // For certificate selection dialog #include #include #include "Util.h" #include "Sign.h" #include "urlmon.h" // for Uri stuff // Input/Ouput file names static const WCHAR g_unsignedFilePath[] = L"SampleMusicBundle.zip"; const WCHAR g_signedFilePath[] = L"SampleMusicBundle_signed.zip"; static const WCHAR g_signatureMethod[] = L"http://www.w3.org/2000/09/xmldsig#rsa-sha1"; static const WCHAR g_defaultDigestMethod[] = L"http://www.w3.org/2000/09/xmldsig#sha1"; static const WCHAR g_signatureOriginPartName[] = L"/package/services/digital-signature/origin.psdsor"; // Content types for parts in music bundle static const WCHAR g_trackContentType[] = L"audio/x-ms-wma"; static const WCHAR g_lyricContentType[] = L"text/plain"; static const WCHAR g_albumArtContentType[] = L"image/jpeg"; static const WCHAR g_tracklistPartContentType[] = L"application/vnd.ms-wpl"; // Relationship types for relationships in music bundle static const WCHAR g_tracklistRelationshipType[] = L"http://schemas.example.com/package/2008/relationships/media-bundle/tracklist"; // We reuse the thumbnail relationship defined in Office Open XML spec for relationship // to the album art image part. static const WCHAR g_albumArtRelationshipType[] = L"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; static const WCHAR g_albumWebsiteRelationshipType[] = L"http://schemas.example.com/package/2008/relationships/media-bundle/album-website"; static LPCWSTR g_trackRelationshipType = L"http://schemas.example.com/package/2008/relationships/media-bundle/playlist-song"; static LPCWSTR g_lyricRelationshipType = L"http://schemas.example.com/package/2008/relationships/media-bundle/song-lryic"; // Signature relationship type as defined in OPC spec static LPCWSTR g_signatureRelationshipType = L"http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature"; // Get the parts that need to be signed according to the signing policy. // Method may return valid values for some of the out parameters even on failure, // and the caller must always free resources if a non-NULL value is returned. // // Parameters: // opcPackage - the package of the parts // trackParts - when the function returns successfully, it holds the arry of track parts. // lyricParts - when the function returns successfully, it holds the arry of lyric parts. // tracklistPart - when the function returns successfully, it holds the tracklist part. // albumArtPart - when the function returns successfully, it holds the album art part. HRESULT GetPartsToBeSigned( IOpcPackage* opcPackage, OpcPartArray* trackParts, OpcPartArray* lyricParts, IOpcPart** tracklistPart, IOpcPart** albumArtPart ) { IOpcPartSet * partSet = NULL; IOpcRelationshipSet * relsSet = NULL; IOpcRelationshipSet * partRelsSet = NULL; IOpcRelationshipEnumerator * partRelsEnumerator = NULL; UINT32 count = 0; BOOL hasNext = FALSE; BOOL hasPrevious = FALSE; HRESULT hr = opcPackage->GetRelationshipSet(&relsSet); if (SUCCEEDED(hr)) { hr = opcPackage->GetPartSet(&partSet); } // Get album art part. As defined by our sample signing policy, it needs to be signed. if (SUCCEEDED(hr)) { hr = GetPartByRelationshipType( partSet, relsSet, g_albumArtRelationshipType, g_albumArtContentType, albumArtPart ); } // Get tracklist part. As defined by our sample signing policy, it needs to be signed. if (SUCCEEDED(hr)) { hr = GetPartByRelationshipType( partSet, relsSet, g_tracklistRelationshipType, g_tracklistPartContentType, tracklistPart ); } // Get tracklist part's relationships. if (SUCCEEDED(hr)) { hr = (*tracklistPart)->GetRelationshipSet(&partRelsSet); } // Get tracklist part's relationships with track relationship type. if (SUCCEEDED(hr)) { hr = partRelsSet->GetEnumeratorForType(g_trackRelationshipType, &partRelsEnumerator); } // Get the number of relationships to track parts. Note that we assume the // number of track parts is the same as the number of lyric parts. while ( SUCCEEDED(hr) && SUCCEEDED(hr = partRelsEnumerator->MoveNext(&hasNext)) && hasNext ) { count++; } // Initialize the OpcPartArrays with desired capacity. if (SUCCEEDED(hr)) { hr = trackParts->Init(count); } if (SUCCEEDED(hr)) { hr = lyricParts->Init(count); } while ( SUCCEEDED(hr) && // There is no method to rewind the enumerator to go to the beginning. // So we will have to go backwards to revisit the elements if we reuse the existing // enumerator. If you don't want to go backwards, you can call Clone() on the // enumerator BEFORE you started to iterate through it. The cloned enumerator // will have the position pointing to the beginning so you can iterate forwards. SUCCEEDED(hr = partRelsEnumerator->MovePrevious(&hasPrevious)) && hasPrevious ) { IOpcRelationship * rels = NULL; IOpcPart * trackPart = NULL; IOpcRelationshipSet * trackPartRelationshipSet = NULL; IOpcPart * lyricPart = NULL; hr = partRelsEnumerator->GetCurrent(&rels); // Get track part. As defined by our sample signing policy, it needs to be signed. if (SUCCEEDED(hr)) { hr = GetRelationshipTargetPart(partSet, rels, g_trackContentType, &trackPart); } if (SUCCEEDED(hr)) { hr = trackParts->Append(trackPart); } if (SUCCEEDED(hr)) { hr = trackPart->GetRelationshipSet(&trackPartRelationshipSet); } // Get lyric part. As defined by our sample signing policy, it needs to be signed. if (SUCCEEDED(hr)) { hr = GetPartByRelationshipType( partSet, trackPartRelationshipSet, g_lyricRelationshipType, g_lyricContentType, &lyricPart ); } if (SUCCEEDED(hr)) { hr = lyricParts->Append(lyricPart); } // Release resources if (rels) { rels->Release(); rels = NULL; } if (trackPart) { trackPart->Release(); trackPart = NULL; } if (trackPartRelationshipSet) { trackPartRelationshipSet->Release(); trackPartRelationshipSet = NULL; } if (lyricPart) { lyricPart->Release(); lyricPart = NULL; } } // Release resources if (partSet) { partSet->Release(); partSet = NULL; } if (relsSet) { relsSet->Release(); relsSet = NULL; } if (partRelsSet) { partRelsSet->Release(); partRelsSet = NULL; } if (partRelsEnumerator) { partRelsEnumerator->Release(); partRelsEnumerator = NULL; } // Depending on file format specification, we may also want to check whether the parts // we want to sign have valid content formats. For example, if the file format says // a specific part cannot be empty, then we should also check whether the part size // is zero. Validating the file format before signing makes sure you sign a package // that is valid according to your file format specification. return hr; } // Adds relationship reference by relationship type. Multiple relationship types // can be added in one call to this method. // // Parameters: // relsReferenceSet - the relationship reference set that the new reference will be added to // sourceUri - source uri of the relationships // count - number of relationship type strings in the relationshipTypes array // relationshipTypes - an array of relationship types HRESULT AddReferenceByRelationshipType( IOpcSignatureRelationshipReferenceSet* relsReferenceSet, IOpcUri* sourceUri, UINT32 count, LPCWSTR relationshipTypes[] ) { // Create the relationship selector set to add relationship types as selection criterion. IOpcRelationshipSelectorSet * relsSelectorSet = NULL; HRESULT hr = relsReferenceSet->CreateRelationshipSelectorSet(&relsSelectorSet); if (SUCCEEDED(hr)) { for (UINT32 i = 0; i < count && SUCCEEDED(hr); i++) { // Add relationship type as as selection criterion to the selector set. // Since we sign the relationships by type, it guards against changes to the // relationships we care about. However, relationships with different types // can be changed, added or removed later and not breaking the signature. If // you want to be more strict, you can sign the entire relationships part // (see OPC_RELATIONSHIP_SIGN_PART) so that any change to the relationships // part will break the signature. hr = relsSelectorSet->Create( OPC_RELATIONSHIP_SELECT_BY_TYPE, relationshipTypes[i], NULL ); } // Create and add the relationship reference to the set. if (SUCCEEDED(hr)) { relsReferenceSet->Create( sourceUri, NULL, // use default digest method, which will be set by IOpcSigningOptions::SetDefaultDigestMethod() OPC_RELATIONSHIP_SIGN_USING_SELECTORS, relsSelectorSet, OPC_CANONICALIZATION_C14N, NULL // no need to get the output reference object ); } } // Release resources if (relsSelectorSet) { relsSelectorSet->Release(); relsSelectorSet = NULL; } return hr; } // This method demonstrates how you can add custom objects to the signature, // and also sign the custom objects. HRESULT AddCustomObjectsToSign( IOpcSigningOptions* signingOptions ) { IOpcSignatureCustomObjectSet * customObjectSet = NULL; IOpcSignatureReferenceSet * customReferenceSet = NULL; IUri * referenceUri1 = NULL; IUri * referenceUri2 = NULL; UINT32 count = 0; // Custom objects allow applications to add extra information to the signature. // One possible usage may be adding trustable timestamp to the signature. // Note that the signing time provided by OPC digital signature spec is from the // clock of local machine where the signature is created. So it is not a trustable // time since the clock may be inaccurate or the user may have changed the time locally. // // Here we use a fake timestamp as an example to show how you can use custom object. // In real world you need to get a trustable timestamp from a timestamp server. // // To better understand this sample, you should open and view the signature XML stored in // the signature part in the signed package. The default file extension of the signature // part is .psdsxs. // Example of constructing a fully signed custom object. The entire // object XML markup will be signed because we will reference the Id of the // object element. // // Note that it is important to include namespace declaration in the XML markup. // Otherwise it is not a valid XML snippet that can be interpreted. UINT8 customObject1[] = "" "This is a fake timestamp." "ABCDEFGHIJK" ""; // Example of custructing a partially signed custom object. UINT8 customObject2[] = "" // The element XML markup will be signed, because we will // reference its Id. "" "This is a fake timestamp." "ABCDEFGHIJK" "" // Outside of the element is the unsigned portion of this custom // object. Updates (changes, addition or deletion) can be made in that portion // without breaking the signature. "" "Unsigned portion of XML. Can be updated without breaking the signature." "" ""; // Get the custom object set from signing options. We will add custom objects to // it so those custom objects will be created in the signature XML after Sign() is called. HRESULT hr = signingOptions->GetCustomObjectSet(&customObjectSet); // Create and add the custom object into the custom object set. if (SUCCEEDED(hr)) { // Number of bytes of the custom object. Note that we need to minus 1 because the // buffer has the last byte as string terminator ('\0'). count = sizeof(customObject1)/sizeof(customObject1[0]) - 1; hr = customObjectSet->Create(customObject1, count, NULL); } // Create and add the custom object into the custom object set. if (SUCCEEDED(hr)) { // Number of bytes of the custom object. Note that we need to minus 1 because the // buffer has the last byte as string terminator ('\0'). count = sizeof(customObject2)/sizeof(customObject2[0]) - 1; hr = customObjectSet->Create(customObject2, count, NULL); } // Adding custom objects into the custom object set will make them appear // in the signature xml. However, unless you also add references to them, // they will not be signed. // // Add reference to the custom objects so they will be signed. if (SUCCEEDED(hr)) { hr = signingOptions->GetCustomReferenceSet(&customReferenceSet); } // Create reference to the first custom object. Note that the reference Uri is // constructed as "#" + the Id value of the element you want to sign. Here // "#idMyCustomObject_Timestamp1" refers to the first custom object. Its entire // XML markup will be signed. if (SUCCEEDED(hr)) { hr = CreateUri(L"#idMyCustomObject_Timestamp1", Uri_CREATE_ALLOW_RELATIVE, 0, &referenceUri1); } if (SUCCEEDED(hr)) { hr = customReferenceSet->Create( referenceUri1, NULL, // Id. No Id for the reference element L"http://www.w3.org/2000/09/xmldsig#Object", // Type NULL, // Digest method. Use default digest method, which will be set by IOpcSigningOptions::SetDefaultDigestMethod() OPC_CANONICALIZATION_C14N, // Use C14N to canonicalize the referenced XML markup before signing NULL // no need to get the output signature reference object ); } // Create reference to the element we want to sign in the second custom object. // Note that the reference Uri is constructed as "#" + the Id value of the element // you want to sign. Here "#idMyCustomObject_Timestamp2" refers to the // element inside of the second custom object. Only the XML markup, including // its nested elements will be signed. if (SUCCEEDED(hr)) { hr = CreateUri( L"#idMyCustomObject_Timestamp2", Uri_CREATE_ALLOW_RELATIVE, 0, &referenceUri2 ); } if (SUCCEEDED(hr)) { hr = customReferenceSet->Create( referenceUri2, NULL, // Id. No Id for the reference element L"http://www.example.com/MusicBundle#TimestampObject", // Type, user defined type NULL, // Digest method. Use default digest method, which will be set by IOpcSigningOptions::SetDefaultDigestMethod() OPC_CANONICALIZATION_C14N, // Use C14N to canonicalize the referenced XML markup before signing NULL // no need to get the output signature reference object ); } // Release resources if (customObjectSet) { customObjectSet->Release(); customObjectSet = NULL; } if (customReferenceSet) { customReferenceSet->Release(); customReferenceSet = NULL; } if (referenceUri1) { referenceUri1->Release(); referenceUri1 = NULL; } if (referenceUri2) { referenceUri2->Release(); referenceUri2 = NULL; } return hr; } // Pop up an certificate selection dialog and let user select a certificate for signing. HRESULT SelectCert( PCCERT_CONTEXT* certContext ) { typedef BOOL (WINAPI *fnCertSelectCertificate)(PCERT_SELECT_STRUCT_W); HMODULE hCryptDlgInst = LoadLibrary(L"CryptDlg.dll"); if (hCryptDlgInst == NULL) { fwprintf(stderr, L"Cannot load CryptDlg.dll.\n"); return HRESULT_FROM_WIN32(GetLastError()); } HRESULT hr = S_OK; fnCertSelectCertificate pfnCertSelectCertificate = (fnCertSelectCertificate)GetProcAddress(hCryptDlgInst, "CertSelectCertificateW"); if (pfnCertSelectCertificate) { HCERTSTORE hMySysStore = CertOpenSystemStore(NULL, L"MY"); if (hMySysStore) { CERT_SELECT_STRUCT certSelect = {0}; certSelect.dwSize = sizeof(CERT_SELECT_STRUCT); certSelect.hInstance = hCryptDlgInst; certSelect.szTitle = L"Select signing certificate."; certSelect.cCertStore = 1; certSelect.arrayCertStore = &hMySysStore; certSelect.cCertContext = 1; certSelect.arrayCertContext = certContext; BOOL result = pfnCertSelectCertificate(&certSelect); if (!result) { hr = HRESULT_FROM_WIN32(GetLastError()); if (SUCCEEDED(hr)) { // The user just clicked "Cancel". fwprintf(stderr, L"You must select a certificate for signing to proceed.\n"); hr = E_FAIL; } else { fwprintf(stderr, L"CertSelectCertificate() failed.\n"); } } CertCloseStore(hMySysStore, 0); } else { fwprintf(stderr, L"CertOpenSystemStore() failed.\n"); hr = HRESULT_FROM_WIN32(GetLastError()); } } else { fwprintf(stderr, L"Cannot find CertSelectCertificateW function in CryptDlg.dll.\n"); hr = HRESULT_FROM_WIN32(GetLastError()); } FreeLibrary(hCryptDlgInst); return hr; } // Adding signing information to signing options to prepare for signing. // // Parameters: // opcFactory - the IOpcFactory instance // opcPackage - the package to sign // signingOptions - the signing options that will be filled in with signing information in this function // opcDigSigManager - the digital signature manager that will control the signing process HRESULT PrepareForSigning( IOpcFactory* opcFactory, IOpcPackage* opcPackage, IOpcSigningOptions* signingOptions, IOpcDigitalSignatureManager* opcDigSigManager ) { IOpcPart * albumArtPart = NULL; IOpcPart * tracklistPart = NULL; IOpcSignaturePartReferenceSet * partReferenceSet = NULL; IOpcSignatureRelationshipReferenceSet * relsReferenceSet = NULL; IOpcUri * rootUri = NULL; IOpcPartUri * albumArtPartUri = NULL; IOpcPartUri * tracklistPartUri = NULL; IOpcPartUri * signatureOriginPartUri = NULL; OpcPartArray trackParts; OpcPartArray lyricParts; // As defined by our sample signing policy, any root relationship with tracklist, // album art or album website relationship type needs to be signed. LPCWSTR rootRelationshipTypesToSign[] = { g_tracklistRelationshipType, g_albumArtRelationshipType, g_albumWebsiteRelationshipType }; // Get the parts that should be signed according to the custom signing policy. HRESULT hr = GetPartsToBeSigned( opcPackage, &trackParts, &lyricParts, &tracklistPart, &albumArtPart ); // Get the part reference set so we can add references to parts we want to sign. if (SUCCEEDED(hr)) { hr = signingOptions->GetSignaturePartReferenceSet(&partReferenceSet); } // Get the relationship reference set so we can add references to relationships we want to sign. if (SUCCEEDED(hr)) { hr = signingOptions->GetSignatureRelationshipReferenceSet(&relsReferenceSet); } // // Create relationship references to root relationships that need to be signed. // if (SUCCEEDED(hr)) { hr = opcFactory->CreatePackageRootUri(&rootUri); } // Add references to root relationships need-to-sign to the relationship reference set. if (SUCCEEDED(hr)) { hr = AddReferenceByRelationshipType(relsReferenceSet, rootUri, 3, rootRelationshipTypesToSign); } // // Create part reference to the album art part so it will be signed. // if (SUCCEEDED(hr)) { hr = albumArtPart->GetName(&albumArtPartUri); } if (SUCCEEDED(hr)) { hr = partReferenceSet->Create( albumArtPartUri, NULL, // use default digest method, which will be set by IOpcSigningOptions::SetDefaultDigestMethod() OPC_CANONICALIZATION_NONE, NULL // no need to get the output part reference ); } // // Create part reference to the tracklist part so it will be signed. // if (SUCCEEDED(hr)) { hr = tracklistPart->GetName(&tracklistPartUri); } if (SUCCEEDED(hr)) { hr = partReferenceSet->Create( tracklistPartUri, NULL, // use default digest method, which will be set by IOpcSigningOptions::SetDefaultDigestMethod() OPC_CANONICALIZATION_NONE, NULL // no need to get the output part reference ); } // // As defined by our sample signing policy, any tracklist part relationship with // track relationship type needs to be signed. // Add a reference to such a relationship to the relationship reference set. // if (SUCCEEDED(hr)) { hr = AddReferenceByRelationshipType( relsReferenceSet, tracklistPartUri, 1, &g_trackRelationshipType ); } if (SUCCEEDED(hr)) { // // Create part reference to the track parts so they will be signed. // UINT32 numberOfTracks = trackParts.GetCount(); for (UINT32 i=0; iGetName(&trackPartUri); } if (SUCCEEDED(hr)) { hr = partReferenceSet->Create( trackPartUri, NULL, // use default digest method, which will be set by IOpcSigningOptions::SetDefaultDigestMethod() OPC_CANONICALIZATION_NONE, NULL // no need to get the output part reference ); } // // As defined by our sample signing policy, any track part relationship with // lyric relationship type needs to be signed. // Add a reference to such a relationship to the relationship reference set. // if (SUCCEEDED(hr)) { hr = AddReferenceByRelationshipType( relsReferenceSet, trackPartUri, 1, &g_lyricRelationshipType ); } if (trackPart) { trackPart->Release(); trackPart = NULL; } if (trackPartUri) { trackPartUri->Release(); trackPartUri = NULL; } } } if (SUCCEEDED(hr)) { // // Create part reference to the lyric parts so they will be signed. // UINT32 numberOfLyrics = lyricParts.GetCount(); for (UINT32 i=0; iGetName(&lyricPartUri); } if (SUCCEEDED(hr)) { hr = partReferenceSet->Create( lyricPartUri, NULL, // use default digest method, which will be set by IOpcSigningOptions::SetDefaultDigestMethod() OPC_CANONICALIZATION_NONE, NULL // no need to get the output part reference ); } if (lyricPart) { lyricPart->Release(); lyricPart = NULL; } if (lyricPartUri) { lyricPartUri->Release(); lyricPartUri = NULL; } } } // // As defined by our sample signing policy, any signature origin part's relationship // with signature relationship type needs to be signed. // In order to do that, we must define the signature origin part's name before calling Sign(). // // Note that signing the signature relationships from the signature origin part is not required // by OPC Digital Signature spec. Doing so will prevent any new signatures from being added to // the signed package in the future. It may be desirable or not depending on user's scenario // and should be called out in the signing policy of the custom file format. In general, if you // want to allow somebody else to sign the package after you sign it (countersign scenario), // then you shouldn't sign the signature origin part's signature relationships. // if (SUCCEEDED(hr)) { hr = opcFactory->CreatePartUri(g_signatureOriginPartName, &signatureOriginPartUri); } // We assume the signature origin part uri is unique in the package. If not, Sign() will fail. if (SUCCEEDED(hr)) { hr = opcDigSigManager->SetSignatureOriginPartName(signatureOriginPartUri); } // Add a reference to signature relationship to the relationship reference set so it will be signed. if (SUCCEEDED(hr)) { hr = AddReferenceByRelationshipType( relsReferenceSet, signatureOriginPartUri, 1, &g_signatureRelationshipType ); } // The default certificate embedding option is OPC_CERTIFICATE_IN_CERTIFICATE_PART, // so if you are fine with the default you don't really need to make this call. // Making it explicitly here to let you know you can change the embedding option by calling // SetCertificateEmbeddingOption(). Using OPC_CERTIFICATE_IN_CERTIFICATE_PART is recommended // because multiple signatures may share the same certificate if the certificate is stored in // a part. if (SUCCEEDED(hr)) { hr = signingOptions->SetCertificateEmbeddingOption(OPC_CERTIFICATE_IN_CERTIFICATE_PART); } // Advanced scenario: adding custom objects to the signature. // Custom objects can add more information (like XAdES timestamp) to the signature // if needed. For basic scenarios, you can skip this step. if (SUCCEEDED(hr)) { hr = AddCustomObjectsToSign(signingOptions); } // User must provide default digest method before calling Sign(). if (SUCCEEDED(hr)) { hr = signingOptions->SetDefaultDigestMethod(g_defaultDigestMethod); } // User must provide signature method before calling Sign(). // User also needs to make sure the certificate he/she selected for signing supports // the signature method. Otherwise Sign() will fail. For more information on how to // find the supported signature methods in a certificate, check the // CryptXmlEnumAlgorithmInfo API. if (SUCCEEDED(hr)) { hr = signingOptions->SetSignatureMethod(g_signatureMethod); } // Release resources if (albumArtPart) { albumArtPart->Release(); albumArtPart = NULL; } if (tracklistPart) { tracklistPart->Release(); tracklistPart = NULL; } if (partReferenceSet) { partReferenceSet->Release(); partReferenceSet = NULL; } if (relsReferenceSet) { relsReferenceSet->Release(); relsReferenceSet = NULL; } if (rootUri) { rootUri->Release(); rootUri = NULL; } if (albumArtPartUri) { albumArtPartUri->Release(); albumArtPartUri = NULL; } if (tracklistPartUri) { tracklistPartUri->Release(); tracklistPartUri = NULL; } if (signatureOriginPartUri) { signatureOriginPartUri->Release(); signatureOriginPartUri = NULL; } return hr; } HRESULT SignMusicBundle( IOpcFactory* opcFactory ) { IOpcPackage * opcPackage = NULL; IOpcDigitalSignatureManager * opcDigSigManager = NULL; IOpcDigitalSignatureEnumerator * signatureEnumerator = NULL; IOpcSigningOptions * signingOptions = NULL; IOpcDigitalSignature * signature = NULL; // Load the input music bundle package. HRESULT hr = ReadPackageFromFile(g_unsignedFilePath, opcFactory, &opcPackage); // Create a digital signature manager for the package. if (SUCCEEDED(hr)) { hr = opcFactory->CreateDigitalSignatureManager(opcPackage, &opcDigSigManager); } // First detect whether the package has been signed previously. // Our signing policy in this sample disallows countersignature (adding more // signatures to an already signed music bundle) so we signed the signature origin part’s // signature relationships by relationship type. Adding new signature will // break the existing signature. // // Note that it is not required or recommended for a signing policy to disallow // countersignature. If your signing policy allows countersignature, you shouldn't // sign the signature origin part’s signature relationships by relationship type. if (SUCCEEDED(hr)) { hr = opcDigSigManager->GetSignatureEnumerator(&signatureEnumerator); } if (SUCCEEDED(hr)) { BOOL hasNext = FALSE; hr = signatureEnumerator->MoveNext(&hasNext); if (SUCCEEDED(hr) && hasNext) { fwprintf( stderr, L"This music bundle has been signed. To sign a music bundle, it must have not been signed before.\n" ); hr = E_UNEXPECTED; } } if (SUCCEEDED(hr)) { hr = opcDigSigManager->CreateSigningOptions(&signingOptions); } // Configure signing options, adding references to parts and relationships that need to be signed. if (SUCCEEDED(hr)) { hr = PrepareForSigning(opcFactory, opcPackage, signingOptions, opcDigSigManager); } // Let user select a certificate from the certificate store on the local machine. // We will use the certificate to sign the music bundle and the signer will be identified // by the certificate. It is not always necessary to pop up a UI dialog for user to select // a signer certificate. In a sever signing scenario (no human interaction), the sever process // may have pre-defined certificate to use or pick a certificate from certificate store using // CertFindCertificateInStore API. // // Note that Sign() does not verify the certificate so user should verify the certificate like // making sure it is not expired, not in revocation lists, etc. User should also verify the // certification path of the certificate to see whether it leads to a trusted root certificate. // The above can be done programmatically in production code. For more information, check // CertGetCertificateChain API. // // Also note that the certificate selected must support the signature method we use in this sample, // which is RSA-SHA1. RSA is the encryption algorithm. A user can check whether a certificate // supports RSA by checking its public key type. SHA1 is the hash algorithm and is not related to // a certificate. Because of these, the signature algorithm can be determined based on // what certificate is used for signing, instead of using an arbitrary one like in this sample. // For more information on how to find supported signature algorithms in a certificate, check the // CryptXmlEnumAlgorithmInfo API. PCCERT_CONTEXT cert = NULL; if (SUCCEEDED(hr)) { hr = SelectCert(&cert); } // Sign() will add signature related parts and relationships to the package object. // If Sign() fails, it may leave unwanted parts and relationships in the package. // However, the changes are not persisted until we call IOpcFactory::WritePackageToStream(). // So you can simply discard the package object if Sign() fails and your original package // file on disk is intact. // // However, if you just created the package from scratch or edited an existing package, // you should save the package before calling Sign() to prevent loss of data in case Sign() // fails. // // For this sample, since the package has no changes that need to be saved at this point, // we will call Sign() immediately. if (SUCCEEDED(hr)) { hr = opcDigSigManager->Sign(cert, signingOptions, &signature); } // Release the CERT_CONTEXT we got from SelectCert(). if (cert) { CertFreeCertificateContext(cert); } // Save the signed music bundle. if (SUCCEEDED(hr)) { hr = WritePackageToFile(g_signedFilePath, opcPackage, opcFactory); } if (SUCCEEDED(hr)) { fwprintf(stdout, L"Music bundle has been signed successfully and saved to %s\n", g_signedFilePath); } // Release resources if (opcPackage) { opcPackage->Release(); opcPackage = NULL; } if (opcDigSigManager) { opcDigSigManager->Release(); opcDigSigManager = NULL; } if (signatureEnumerator) { signatureEnumerator->Release(); signatureEnumerator = NULL; } if (signingOptions) { signingOptions->Release(); signingOptions = NULL; } if (signature) { signature->Release(); signature = NULL; } return hr; } //