首页 > 解决方案 > 如何使用基于证书的身份验证创建 Azure blob n 使用 java 下载文件

问题描述

我编写了 java 程序,它连接到我创建的 Azure blob 存储并使用下面的程序下载文件内容,但我的实际产品场景不同,客户端共享了 ThumbPrint、ClientId、AzureKeyVaultUrl、SecretId 和 containerName 以及证书。我的程序在我创建的具有试用期的帐户上运行良好。但是不明白如何使用基于证书的身份验证创建帐户,在使用 java 程序连接时使用该帐户。包com;

import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.*;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.util.Scanner;

public class ConnectToAzureToExistingContainer {
    public static final String storageConnectionString =
            "DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=yyy;EndpointSuffix=zzzz";


    public static void main( String[] args )
    {
        CloudStorageAccount storageAccount;
        CloudBlobClient blobClient = null;
        CloudBlobContainer container=null;
        try {
            storageAccount = CloudStorageAccount.parse(storageConnectionString);
            blobClient = storageAccount.createCloudBlobClient();
            container = blobClient.getContainerReference("revenuestream");
            CloudBlockBlob blob = container.getBlockBlobReference("revenuestreams.csv");
            System.out.println(blob.downloadText());
            System.out.println("Done...");
        }
        catch (StorageException ex){
            System.out.println(String.format("Error returned from the service. Http code: %d and error code: %s", ex.getHttpStatusCode(), ex.getErrorCode()));
        }
        catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

标签: javaazurecertificateazure-blob-storage

解决方案


正如@Thomas 所说,您可以使用 Azure AD 获取访问令牌来访问您的存储文件。主要机制可以参考这个文档。您需要获取访问令牌来调用存储 API以访问您的存储服务。并且您想使用证书来获取此访问令牌以访问存储服务。您可以按照以下步骤操作。

  1. 准备您的 .cer 证书和 .pfx 证书并将您的 .cer 上传到您的 Azure AD 应用程序: 在此处输入图像描述

  2. 运行此 ps 以获取 .pfx 文件的 x5t 值,我们在签署 jwt 令牌时将需要它:

$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("path of your .pfx file","password of your .pfx file",'Exportable')
$x5t = [System.Convert]::ToBase64String($cer.GetCertHash()) 
$x5t

  1. 将 .pfx 文件转换为 .der 文件,以便我们可以在 Java 中轻松使用它:

    1)从 pfx 转换 Pem 文件:

    openssl pkcs12 -in "你的 .pfx 文件路径" -out "新的 .pem 文件路径" -clcerts

    2)将 Pem 转换为 der 以便 java 可以轻松读取它:

    openssl pkcs8 -topk8 -inform PEM -outform DER -in "pem 文件路径" -out "new der 文件路径" -nocrypt

  2. 请按照以下代码从 Azure AD 获取访问令牌:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.naming.ServiceUnavailableException;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientAssertion;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class PublicClient {

    private final static String TENANT_ID = "your tanant id";
    private final static String AUTHORITY = "https://login.microsoftonline.com/" + TENANT_ID;
    private final static String CLIENT_ID = "your Azure AD app ID";
    private final static String X5TVALUE_STRING = "x5t value we get from step2 "; 
    private final static String DERFILE_PATH_STRING = "der file path";

    public static void main(String args[]) throws Exception {
        // Request access token from AAD
        AuthenticationResult result = getAccessToken();

        System.out.print(result.getAccessToken());

    }

    public static PrivateKey getPrivateKey() throws Exception {
        byte[] keyBytes = Files.readAllBytes(Paths.get(DERFILE_PATH_STRING));
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(spec);
    }

    private static AuthenticationResult getAccessToken() throws Exception {
        AuthenticationContext context;
        AuthenticationResult result;
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(1);
            context = new AuthenticationContext(AUTHORITY, false, service);

            PrivateKey key = getPrivateKey();
            String jwt = Jwts.builder().setHeaderParam("typ", "JWT").setHeaderParam("alg", "RS256")
                    .setHeaderParam("x5t",X5TVALUE_STRING).setSubject(CLIENT_ID)
                    .setExpiration(new Date(System.currentTimeMillis() + 200000)).setIssuer(CLIENT_ID)
                    .setNotBefore(new Date())
                    .setAudience("https://login.microsoftonline.com/" + TENANT_ID + "/oauth2/token")
                    .setId(UUID.randomUUID().toString()).signWith(SignatureAlgorithm.RS256, key).compact();

            ClientAssertion clientAssertion = new ClientAssertion(jwt);
            
            Future<AuthenticationResult> future = context.acquireToken("https://storage.azure.com/", clientAssertion,
                    null);

            result = future.get();
        } finally {
            service.shutdown();
        }

        if (result == null) {
            throw new ServiceUnavailableException("authentication result was null");
        }
        return result;
    }

}

使用此令牌,我们可以调用 storage REST API,请注意,如果您使用 Azure AD auth 访问您的存储,则请求标头中需要“x-ms-version: 2017-11-09”: 在此处输入图像描述


推荐阅读