admin管理员组

文章数量:1122833

This is the final working token generator:

std::string crypto_GenerateSasToken(
            const std::string& permissions,
            const std::string& blobName, 
            const std::string& accessKey, 
            const std::string& containerName, 
            const std::string& accountName) 
{
    // Define SAS token parameters
    auto currentTime = std::chrono::system_clock::now();
    auto currentTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>(currentTime.time_since_epoch());
    auto expirationTime = currentTimeSeconds + std::chrono::minutes(120);

    // Convert time to ISO 8601 format
    std::string signedStart  = TimePointToString(currentTimeSeconds);
    std::string signedExpiry = TimePointToString(expirationTime);

    // Define other SAS parameters
    std::string signedPermissions = permissions;
    std::string signedService     = (blobName.empty()) ? "c" : "b"; // 'c' for container, 'b' for blob
    std::string signedProtocol    = "https";
    std::string signedVersion     = "2022-11-02";
    std::string signedResourceType = ""; //  Specifies the resource type (s, c, o for service, container, or object).

    // Create canonicalized resource
    std::string canonicalizedResource;
    if (blobName.empty()) {
        // Use container-level resource if blobName is empty
        canonicalizedResource = "/blob/" + accountName + "/" + containerName;
    } else {
        // Use blob-level resource if blobName is not empty
        canonicalizedResource = "/blob/" + accountName + "/" + containerName + "/" + blobName;
    }

    // Construct the string to sign
    std::ostringstream stringToSign;
    stringToSign << signedPermissions << "\n"
                 << signedStart << "\n"
                 << signedExpiry << "\n"
                 << canonicalizedResource << "\n"
                 << "\n"                         // signedIdentifier (empty)
                 << "\n"                         // signedIP (empty)
                 << signedProtocol << "\n"
                 << signedVersion << "\n"
                 << signedService << "\n"
                 << signedResourceType << "\n"  // signedResourceType (if not used, empty)
                 << "\n"
                 << "\n"
                 << "\n"
                 << "\n"
                 << "\n";
   //        std::cout << "String to sign: '" << stringToSign.str() << "'" << std::endl;

    // Generate the signature
    std::string signature = GenerateSas(accessKey, stringToSign.str());

    // URL encode the signature
    std::string urlEncodedSignature = UrlEncode(signature);

    // Construct the SAS token
    std::ostringstream sasToken;
  
    sasToken 
        << "sp="   << signedPermissions
        << "&st="  << signedStart
        << "&se="  << signedExpiry
        << "&spr=" << signedProtocol
        << "&sv="  << signedVersion
        << "&sr="  << signedService
        << "&sig=" << urlEncodedSignature;
    
//        std::cout << "Blob Service URL: https://" << accountName << ".blob.core.windows/"
//            << containerName << "/" << blobName << "?" << sasToken.str() << std::endl;

     return sasToken.str();
}

I am creating a new rather or follow-up question: "Unable to generate a valid Azure Storage SAS token in Cpp".

I have used the method shown by [Venkatesan]. I'm sure I had a working version at some point, but no more.

<Error>
AuthenticationFailed</Code>
<Message>
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:b56dca11-101e-0036-08f4-3c47cd000000 Time:2024-11-22T15:41:17.0196662Z
</Message>
<AuthenticationErrorDetail>
Signature did not match. String to sign used was rwl 2024-11-22T15:38:31Z 2024-11-22T17:38:31Z /blob/fotacontainer/fota/testfile.txt https 2022-11-02 b
</AuthenticationErrorDetail>
</Error>

The resulting URL is:

.txt?sv=2022-11-02&sr=b&sp=rwl&st=2024-11-22T15:38:31Z&se=2024-11-22T17:38:31Z&spr=https&sig=EMswjhCIzPCd8OBU48OV2suiJI4HNKl94rB5ctyaGGA%3d 

Updated code:

std::string GenerateSas(const std::string& storageAccountKey, const std::string& input) {
    std::string decodedKey;
    StringSource(storageAccountKey, true, new Base64Decoder(new StringSink(decodedKey)));

    // Create HMAC-SHA256 signature
    std::string digest;
    CryptoPP::HMAC<CryptoPP::SHA256> hmac((const byte*)decodedKey.data(), decodedKey.size());
    StringSource(input, true,
        new HashFilter(hmac, new StringSink(digest))); // Compute HMAC digest

    // Encode the digest to Base64
    std::string encodedDigest;
    StringSource(digest, true, new Base64Encoder(new StringSink(encodedDigest), false));
    encodedDigest.erase(std::remove(encodedDigest.begin(), encodedDigest.end(), '\n'), encodedDigest.end());
    return encodedDigest; 
}

std::string TimePointToString(const std::chrono::seconds& timePoint) {
    std::time_t t = timePoint.count();
    std::tm tm{};
    gmtime_r(&t, &tm);
    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
    return oss.str();
}

std::string crypto_GenerateSasToken(
                const std::string& permissions,
                const std::string& blobName, 
                const std::string& accessKey, 
                const std::string& containerName, 
                const std::string& accountName) 
    {
        // Define SAS token parameters
        auto currentTime = std::chrono::system_clock::now();
        auto currentTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>(currentTime.time_since_epoch());
        auto expirationTime = currentTimeSeconds + std::chrono::minutes(120);

        // Convert time to ISO 8601 format
        std::string signedStart = TimePointToString(currentTimeSeconds);
        std::string signedExpiry = TimePointToString(expirationTime);

        // Define other SAS parameters
        std::string signedPermissions = permissions; //"racwdxl";
        std::string signedService = "b"; //(blobName.empty()) ? "c" : "b"; // 'c' for container, 'b' for blob
        std::string signedProtocol = "https";
        std::string signedVersion = "2022-11-02";

        // Create canonicalized resource
        std::string canonicalizedResource;
        if (blobName.empty()) {
            // Use container-level resource if blobName is empty
            canonicalizedResource = "/blob/" + accountName + "/" + containerName;
        } else {
            // Use blob-level resource if blobName is not empty
            canonicalizedResource = "/blob/" + accountName + "/" + containerName + "/" + blobName;
        }

        // Construct the string to sign
        std::ostringstream stringToSign;
        stringToSign << signedPermissions << "\n"
                     << signedStart << "\n"
                     << signedExpiry << "\n"
                     << canonicalizedResource << "\n"
                     << "\n"
                     << "\n"
                     << signedProtocol << "\n"
                     << signedVersion << "\n"
                     << signedService << "\n"
                     << "\n"
                     << "\n"
                     << "\n"
                     << "\n"
                     << "\n" ;

        // Generate the signature
        std::string signature = GenerateSas(accessKey, stringToSign.str());

        // URL encode the signature
        std::string urlEncodedSignature = UrlEncode(signature);

        // Construct the SAS token
        std::ostringstream sasToken;
        sasToken 
            << "sv="   << signedVersion
            << "&sr="  << signedService
            << "&sp="  << signedPermissions
            << "&st="  << signedStart
            << "&se="  << signedExpiry
            << "&spr=" << signedProtocol
            << "&sig=" << urlEncodedSignature;
        
        std::cout << "Blob URL: https://" << accountName << ".blob.core.windows/"
            << containerName << "/" << blobName << "?" << sasToken.str() << std::endl;

        return sasToken.str();
    }

// URL encode function
std::string UrlEncode(const std::string& str) {
    std::ostringstream escaped;
    for (char c : str) {
        if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
            escaped << c;
        }
        else {
            escaped << '%' << std::setw(2) << std::setfill('0') << std::hex << (int)(unsigned char)c;
        }
    }
    return escaped.str();
};

I would really appreciate a second look at this version! Should the "&s__=" field order match the string-to-sign?

This is the final working token generator:

std::string crypto_GenerateSasToken(
            const std::string& permissions,
            const std::string& blobName, 
            const std::string& accessKey, 
            const std::string& containerName, 
            const std::string& accountName) 
{
    // Define SAS token parameters
    auto currentTime = std::chrono::system_clock::now();
    auto currentTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>(currentTime.time_since_epoch());
    auto expirationTime = currentTimeSeconds + std::chrono::minutes(120);

    // Convert time to ISO 8601 format
    std::string signedStart  = TimePointToString(currentTimeSeconds);
    std::string signedExpiry = TimePointToString(expirationTime);

    // Define other SAS parameters
    std::string signedPermissions = permissions;
    std::string signedService     = (blobName.empty()) ? "c" : "b"; // 'c' for container, 'b' for blob
    std::string signedProtocol    = "https";
    std::string signedVersion     = "2022-11-02";
    std::string signedResourceType = ""; //  Specifies the resource type (s, c, o for service, container, or object).

    // Create canonicalized resource
    std::string canonicalizedResource;
    if (blobName.empty()) {
        // Use container-level resource if blobName is empty
        canonicalizedResource = "/blob/" + accountName + "/" + containerName;
    } else {
        // Use blob-level resource if blobName is not empty
        canonicalizedResource = "/blob/" + accountName + "/" + containerName + "/" + blobName;
    }

    // Construct the string to sign
    std::ostringstream stringToSign;
    stringToSign << signedPermissions << "\n"
                 << signedStart << "\n"
                 << signedExpiry << "\n"
                 << canonicalizedResource << "\n"
                 << "\n"                         // signedIdentifier (empty)
                 << "\n"                         // signedIP (empty)
                 << signedProtocol << "\n"
                 << signedVersion << "\n"
                 << signedService << "\n"
                 << signedResourceType << "\n"  // signedResourceType (if not used, empty)
                 << "\n"
                 << "\n"
                 << "\n"
                 << "\n"
                 << "\n";
   //        std::cout << "String to sign: '" << stringToSign.str() << "'" << std::endl;

    // Generate the signature
    std::string signature = GenerateSas(accessKey, stringToSign.str());

    // URL encode the signature
    std::string urlEncodedSignature = UrlEncode(signature);

    // Construct the SAS token
    std::ostringstream sasToken;
  
    sasToken 
        << "sp="   << signedPermissions
        << "&st="  << signedStart
        << "&se="  << signedExpiry
        << "&spr=" << signedProtocol
        << "&sv="  << signedVersion
        << "&sr="  << signedService
        << "&sig=" << urlEncodedSignature;
    
//        std::cout << "Blob Service URL: https://" << accountName << ".blob.core.windows.net/"
//            << containerName << "/" << blobName << "?" << sasToken.str() << std::endl;

     return sasToken.str();
}

I am creating a new rather or follow-up question: "Unable to generate a valid Azure Storage SAS token in Cpp".

I have used the method shown by [Venkatesan]. I'm sure I had a working version at some point, but no more.

<Error>
AuthenticationFailed</Code>
<Message>
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:b56dca11-101e-0036-08f4-3c47cd000000 Time:2024-11-22T15:41:17.0196662Z
</Message>
<AuthenticationErrorDetail>
Signature did not match. String to sign used was rwl 2024-11-22T15:38:31Z 2024-11-22T17:38:31Z /blob/fotacontainer/fota/testfile.txt https 2022-11-02 b
</AuthenticationErrorDetail>
</Error>

The resulting URL is:

https://fotacontainer.blob.core.windows.net/fota/testfile.txt?sv=2022-11-02&sr=b&sp=rwl&st=2024-11-22T15:38:31Z&se=2024-11-22T17:38:31Z&spr=https&sig=EMswjhCIzPCd8OBU48OV2suiJI4HNKl94rB5ctyaGGA%3d 

Updated code:

std::string GenerateSas(const std::string& storageAccountKey, const std::string& input) {
    std::string decodedKey;
    StringSource(storageAccountKey, true, new Base64Decoder(new StringSink(decodedKey)));

    // Create HMAC-SHA256 signature
    std::string digest;
    CryptoPP::HMAC<CryptoPP::SHA256> hmac((const byte*)decodedKey.data(), decodedKey.size());
    StringSource(input, true,
        new HashFilter(hmac, new StringSink(digest))); // Compute HMAC digest

    // Encode the digest to Base64
    std::string encodedDigest;
    StringSource(digest, true, new Base64Encoder(new StringSink(encodedDigest), false));
    encodedDigest.erase(std::remove(encodedDigest.begin(), encodedDigest.end(), '\n'), encodedDigest.end());
    return encodedDigest; 
}

std::string TimePointToString(const std::chrono::seconds& timePoint) {
    std::time_t t = timePoint.count();
    std::tm tm{};
    gmtime_r(&t, &tm);
    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
    return oss.str();
}

std::string crypto_GenerateSasToken(
                const std::string& permissions,
                const std::string& blobName, 
                const std::string& accessKey, 
                const std::string& containerName, 
                const std::string& accountName) 
    {
        // Define SAS token parameters
        auto currentTime = std::chrono::system_clock::now();
        auto currentTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>(currentTime.time_since_epoch());
        auto expirationTime = currentTimeSeconds + std::chrono::minutes(120);

        // Convert time to ISO 8601 format
        std::string signedStart = TimePointToString(currentTimeSeconds);
        std::string signedExpiry = TimePointToString(expirationTime);

        // Define other SAS parameters
        std::string signedPermissions = permissions; //"racwdxl";
        std::string signedService = "b"; //(blobName.empty()) ? "c" : "b"; // 'c' for container, 'b' for blob
        std::string signedProtocol = "https";
        std::string signedVersion = "2022-11-02";

        // Create canonicalized resource
        std::string canonicalizedResource;
        if (blobName.empty()) {
            // Use container-level resource if blobName is empty
            canonicalizedResource = "/blob/" + accountName + "/" + containerName;
        } else {
            // Use blob-level resource if blobName is not empty
            canonicalizedResource = "/blob/" + accountName + "/" + containerName + "/" + blobName;
        }

        // Construct the string to sign
        std::ostringstream stringToSign;
        stringToSign << signedPermissions << "\n"
                     << signedStart << "\n"
                     << signedExpiry << "\n"
                     << canonicalizedResource << "\n"
                     << "\n"
                     << "\n"
                     << signedProtocol << "\n"
                     << signedVersion << "\n"
                     << signedService << "\n"
                     << "\n"
                     << "\n"
                     << "\n"
                     << "\n"
                     << "\n" ;

        // Generate the signature
        std::string signature = GenerateSas(accessKey, stringToSign.str());

        // URL encode the signature
        std::string urlEncodedSignature = UrlEncode(signature);

        // Construct the SAS token
        std::ostringstream sasToken;
        sasToken 
            << "sv="   << signedVersion
            << "&sr="  << signedService
            << "&sp="  << signedPermissions
            << "&st="  << signedStart
            << "&se="  << signedExpiry
            << "&spr=" << signedProtocol
            << "&sig=" << urlEncodedSignature;
        
        std::cout << "Blob URL: https://" << accountName << ".blob.core.windows.net/"
            << containerName << "/" << blobName << "?" << sasToken.str() << std::endl;

        return sasToken.str();
    }

// URL encode function
std::string UrlEncode(const std::string& str) {
    std::ostringstream escaped;
    for (char c : str) {
        if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
            escaped << c;
        }
        else {
            escaped << '%' << std::setw(2) << std::setfill('0') << std::hex << (int)(unsigned char)c;
        }
    }
    return escaped.str();
};

I would really appreciate a second look at this version! Should the "&s__=" field order match the string-to-sign?

Share Improve this question edited Nov 23, 2024 at 23:57 Tom 51.5k2 gold badges18 silver badges34 bronze badges asked Nov 22, 2024 at 16:07 Stefan GudmundssonStefan Gudmundsson 32 bronze badges 3
  • 1 Question: should the order of the "&s__=" tokens not match the order of the same items as in string-to-sign? – Stefan Gudmundsson Commented Nov 22, 2024 at 16:47
  • Finally found the correct token format! – Stefan Gudmundsson Commented Nov 22, 2024 at 20:56
  • It's great to hear that you were able to resolve your issue. Could you please document your solution in an answer? It would provide clarity for those encountering similar technical challenges within the community – Venkatesan Commented Nov 23, 2024 at 5:44
Add a comment  | 

1 Answer 1

Reset to default 0

Here is my solution:

std::string crypto_GenerateSasToken(
            const std::string& permissions,
            const std::string& blobName, 
            const std::string& accessKey, 
            const std::string& containerName, 
            const std::string& accountName) 
{
    // Define SAS token parameters
    auto currentTime = std::chrono::system_clock::now();
    auto currentTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>(currentTime.time_since_epoch());
    auto expirationTime = currentTimeSeconds + std::chrono::minutes(120);

    // Convert time to ISO 8601 format
    std::string signedStart  = TimePointToString(currentTimeSeconds);
    std::string signedExpiry = TimePointToString(expirationTime);

    // Define other SAS parameters
    std::string signedPermissions = permissions;
    std::string signedService     = (blobName.empty()) ? "c" : "b"; // 'c' for container, 'b' for blob
    std::string signedProtocol    = "https";
    std::string signedVersion     = "2022-11-02";
    std::string signedResourceType = ""; //  Specifies the resource type (s, c, o for service, container, or object).

    // Create canonicalized resource
    std::string canonicalizedResource;
    if (blobName.empty()) {
        // Use container-level resource if blobName is empty
        canonicalizedResource = "/blob/" + accountName + "/" + containerName;
    } else {
        // Use blob-level resource if blobName is not empty
        canonicalizedResource = "/blob/" + accountName + "/" + containerName + "/" + blobName;
    }

    // Construct the string to sign
    std::ostringstream stringToSign;
    stringToSign << signedPermissions << "\n"
                 << signedStart << "\n"
                 << signedExpiry << "\n"
                 << canonicalizedResource << "\n"
                 << "\n"                         // signedIdentifier (empty)
                 << "\n"                         // signedIP (empty)
                 << signedProtocol << "\n"
                 << signedVersion << "\n"
                 << signedService << "\n"
                 << signedResourceType << "\n"  // signedResourceType (if not used, empty)
                 << "\n"
                 << "\n"
                 << "\n"
                 << "\n"
                 << "\n";


    // Generate the signature
    std::string signature = GenerateSas(accessKey, stringToSign.str());

    // URL encode the signature
    std::string urlEncodedSignature = UrlEncode(signature);

    // Construct the SAS token
    std::ostringstream sasToken;
  
    sasToken 
        << "sp="   << signedPermissions
        << "&st="  << signedStart
        << "&se="  << signedExpiry
        << "&spr=" << signedProtocol
        << "&sv="  << signedVersion
        << "&sr="  << signedService
        << "&sig=" << urlEncodedSignature;
    
     return sasToken.str();
}

本文标签: cUnable to generate a valid Azure Storage SAS token in CppUpdated codeStack Overflow