當創建密鑰材料來源為外部的非對稱密鑰時,KMS不會生成密鑰材料,您需要導入自己的密鑰材料。本文介紹如何為非對稱密鑰導入密鑰材料。
如果您的軟件密鑰管理實例、硬件密鑰管理實例不支持導入密鑰材料,或者導入密鑰材料時返回失敗,請聯系阿里云技術支持升級實例。
功能介紹
密鑰是KMS的基本資源,由密鑰ID、基本元數據(如密鑰狀態等)以及密鑰材料組成。創建密鑰時,您可以選擇由KMS生成密鑰材料,也可以選擇外部來源的密鑰材料。如果選擇了外部來源的密鑰材料,您需要將外部密鑰材料導入到密鑰中,該功能通常被稱為自帶密鑰(BYOK)。
不同KMS密鑰管理類型對導入密鑰材料的支持情況,請參見下表。關于密鑰管理類型的更多信息,請參見密鑰管理類型和密鑰規格。
:表示支持導入相應的密鑰材料。
:表示不支持導入相應的密鑰材料。
密鑰管理類型 | 導入對稱密鑰材料 | 導入非對稱密鑰材料 |
默認密鑰 |
|
|
軟件密鑰 | √ | √ |
硬件密鑰 | √ | √ |
注意事項
為密鑰首次導入密鑰材料后,密鑰即和該密鑰材料綁定,不再支持導入其他密鑰材料。
您可以根據需要將相同的密鑰材料多次導入到KMS密鑰中,但不能將不同的密鑰材料導入到一個KMS密鑰。
如果密鑰的密鑰材料過期或被刪除,您可以為密鑰再次導入相同的密鑰材料,使得該密鑰再次可用,導入密鑰材料后不支持導出,因此請您妥善保管密鑰材料。
前提條件
已購買和啟用KMS實例。具體操作,請參見購買和啟用KMS實例。
通過控制臺導入密鑰材料
步驟一:創建非對稱密鑰
導入密鑰材料前,請創建密鑰材料來源為外部的非對稱密鑰。
軟件密鑰
登錄密鑰管理服務控制臺,在頂部菜單欄選擇地域后,在左側導航欄單擊 。
在用戶主密鑰頁簽,實例ID選擇軟件密鑰管理實例,單擊創建密鑰。
在創建密鑰面板,完成配置項設置,然后單擊確定。
配置項
說明
密鑰類型
選擇非對稱密鑰。
密鑰規格
非對稱密鑰規格:RSA_2048、RSA_3072、EC_P256、EC_P256K
密鑰用途
密鑰的用途。取值:
Encrypt/Decrypt:數據加密和解密。
Sign/Verify:產生和驗證數字簽名。
密鑰別名
密鑰的別名標識符。支持英文字母、數字、下劃線(_)、短劃線(-)和正斜線(/)。
標簽
密鑰的標簽,方便您對密鑰進行分類管理。每個標簽由一個鍵值對(Key:Value)組成,包含標簽鍵(Key)、標簽值(Value)。
說明標簽鍵和標簽值的格式:最多支持128個字符,可以包含英文大小寫字母、數字、正斜線(/)、反斜線(\)、下劃線(_)、短劃線(-)、半角句號(.)、加號(+)、等于號(=)、半角冒號(:)、字符at(@)、空格。
標簽鍵不能以aliyun或acs:開頭。
每個密鑰最多可以設置20個標簽鍵值對。
描述信息
密鑰的說明信息。
高級選項
策略配置:詳細信息,請參見密鑰策略概述。
密鑰材料來源:選擇外部(導入密鑰材料)。
說明請仔細閱讀并選中我了解使用外部密鑰材料的方法和意義。
硬件密鑰
登錄密鑰管理服務控制臺,在頂部菜單欄選擇地域后,在左側導航欄單擊 。
在用戶主密鑰頁簽,實例ID選擇硬件密鑰管理實例,單擊創建密鑰。
在創建密鑰面板,完成配置項設置,然后單擊確定。
配置項
說明
密鑰類型
選擇非對稱密鑰。
密鑰規格
非對稱密鑰規格:RSA_2048、RSA_3072、RSA_4096、EC_P256、EC_P256K、EC_SM2
密鑰用途
密鑰的用途。取值:
Encrypt/Decrypt:數據加密和解密。
Sign/Verify:產生和驗證數字簽名。
密鑰別名
密鑰的別名標識符。支持英文字母、數字、下劃線(_)、短劃線(-)和正斜線(/)。
標簽
密鑰的標簽,方便您對密鑰進行分類管理。每個標簽由一個鍵值對(Key:Value)組成,包含標簽鍵(Key)、標簽值(Value)。
說明標簽鍵和標簽值的格式:最多支持128個字符,可以包含英文大小寫字母、數字、正斜線(/)、反斜線(\)、下劃線(_)、短劃線(-)、半角句號(.)、加號(+)、等于號(=)、半角冒號(:)、字符at(@)、空格。
標簽鍵不能以aliyun或acs:開頭。
每個密鑰最多可以設置20個標簽鍵值對。
描述信息
密鑰的說明信息。
高級選項
策略配置:詳細信息,請參見密鑰策略概述。
密鑰材料來源:選擇外部(導入密鑰材料)。
說明請仔細閱讀并選中我了解使用外部密鑰材料的方法和意義。
步驟二:下載包裝公鑰和導入令牌
導入密鑰材料的參數包含包裝公鑰和導入令牌,包裝公鑰用于加密密鑰材料,在導入過程中保護您的密鑰材料,導入令牌用于導入密鑰材料。
定位到目標密鑰,單擊操作列的詳情,在密鑰詳情頁面的密鑰材料區域,單擊獲取導入參數。
在獲取導入密鑰材料的參數對話框,選擇公鑰類型、加密算法后,單擊下一步。
密鑰管理類型
KMS密鑰的規格
包裝公鑰類型
加密算法
軟件密鑰
RSA_2048
RSA_3072
EC_P256
EC_P256K
RSA_2048
RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD
硬件密鑰
RSA_2048
RSA_3072
RSA_4096
EC_P256
EC_P256K
RSA_2048
RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD
EC_SM2
EC_SM2
SM2PKE
RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD:具體加密過程,請參見示例:使用OPENSSL生成RSA_2048算法的密鑰材料。
SM2PKE:SM2橢圓曲線公鑰加密算法,符合標準《GM/T 0003.4 SM2 橢圓曲線公鑰密碼算法 第4部分:公鑰加密算法》
下載包裝公鑰以及導入令牌,并妥善保存。
公鑰格式:
der格式:下載后文件名默認為publickey_******.bin。
pem格式:下載后文件名默認為publickey_******.pem。
導入令牌:下載后文件名默認為token_******.txt。
重要導入令牌的有效期為24小時,在有效期內可以重復使用,失效后需要獲取新的導入令牌和公鑰。
包裝公鑰和導入令牌必須配套使用。即不允許下載兩次包裝公鑰和導入令牌,使用其中一個的包裝公鑰,另一個的導入令牌。
步驟三:使用包裝公鑰加密密鑰材料
請在您的系統環境中生成并加密密鑰材料,過程中會使用到以下密鑰,具體說明請參見下表。
密鑰 | 用途 | 提供者 | 標記說明 |
目標非對稱密鑰TAK(Target Asymmetric Key) | 待導入的目標非對稱密鑰。 | 您的系統環境或者工具(如線下的密鑰管理設施KMI,或者線下的硬件安全模塊HSM)。 |
|
加密密鑰IWK(Import Wrapping Key) | 用于導入TAK的加密密鑰。 | 阿里云KMS。 |
|
瞬時密鑰ESK(Ephemeral Symmetric Key) | 一個瞬時存在的對稱密鑰,用于直接加密TAKpriv。 | 源環境的系統或者工具,在完成對TAK的導出操作后請立即銷毀。 | 不涉及 |
創建一個目標非對稱密鑰私鑰(TAKpriv),密鑰規格與您創建非對稱密鑰時選擇的密鑰規格一致。如果您已有目標非對稱密鑰私鑰(TAKpriv),請跳過本步驟。
說明TAKpriv格式需要遵循:RSA私鑰根據RFC3447進行編碼,ECC私鑰根據RFC5915進行編碼,然后根據RFC5208包裝為PKCS#8格式。
創建一個瞬時密鑰(ESK)。
使用加密密鑰公鑰(IWKpub)來加密瞬時密鑰(ESK),得到瞬時密鑰密文(Cipher(ESK))。
使用瞬時密鑰(ESK)加密目標非對稱密鑰私鑰(TAKpriv),得到目標非對稱密鑰的私鑰密文(Cipher(TAKpriv))。
按照Cipher(ESK)||Cipher(TAKpriv)格式組裝結果數據,得到加密后的密鑰材料。
創建一個SM2算法的目標非對稱密鑰私鑰(TAKpriv),并獲取私鑰的橢圓曲線的參數D。
#生成SM2私鑰。 gmssl ecparam -name sm2p256v1 -genkey -outform DER -out TakSm2Priv.bin #查看私鑰的D值。 gmssl asn1parse -inform DER -in TakSm2Priv.bin #將D值寫入文件。 echo 'CB55E5ECC20BF7E9249ADEDE990EF141D14252E024734EB058A6B9F103F12A04' | xxd -r -p > TakSm2D.bin
創建一個SM4算法的瞬時密鑰(ESK)。
gmssl rand -out EskSm4.bin 16
使用加密密鑰公鑰(IWKpub),采用SM2算法加密瞬時密鑰(ESK),得到瞬時密鑰密文(Cipher(ESK))。
gmssl sm2utl -encrypt -in EskSm4.bin -pubin -inkey PublicKey.pem -out CipherEsk.bin
說明請將PublicKey.pem替換為您在密鑰管理服務控制臺下載的公鑰文件的名稱。
使用瞬時密鑰(ESK)加密目標非對稱密鑰私鑰(TAKpriv)的橢圓曲線參數D ,生成目標非對稱密鑰的私鑰密文(Cipher(TAKpriv))。其中加密模式為ECB,填充模式為NoPadding。
xxd -l 16 -c 16 -ps EskSm4.bin | xargs -I {} openssl enc -sms4-ecb -e -K {} -in TakSm2D.bin -nosalt -nopad -out CipherTakPriv.bin
按照Cipher(ESK) || Cipher(TAKpriv)格式組裝結果數據,得到加密后的密鑰材料。
cat CipherEsk.bin CipherTakPriv.bin > EncryptedKeyMaterial.bin
創建一個RSA_2048算法的目標非對稱密鑰私鑰,并且將私鑰轉為PKCS#8格式。
openssl genrsa -out TakPrivPkcs1.pem 2048 openssl pkcs8 -topk8 -inform PEM -in TakPrivPkcs1.pem -outform der -nocrypt -out TakPrivPkcs8.bin
創建一個AES_256算法的瞬時密鑰(ESK)。
openssl rand -out EskAes256.bin 32
使用加密密鑰公鑰(IWKpub)加密瞬時密鑰(ESK),得到瞬時密鑰密文(Cipher(ESK))。加密時采用RSAES OAEP標準加密,其中MGF1和哈希算法為SHA256。
openssl pkeyutl -encrypt -pubin -inkey PublicKey.pem -in EskAes256.bin -pkeyopt \ rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -out \ CipherEsk.bin
說明請將PublicKey.pem替換為您在密鑰管理服務控制臺下載的公鑰文件的名稱。
使用瞬時密鑰(ESK)加密目標非對稱密鑰私鑰(TAKpriv),生成目標非對稱密鑰的私鑰密文(Cipher(TAKpriv))。加密模式為ECB,填充模式為PKCS#7 Padding。
xxd -l 32 -c 32 -ps EskAes256.bin | xargs -I {} openssl enc -aes-256-ecb -e -K {} -in TakPrivPkcs8.bin -nosalt -out CipherTakPriv.bin
按照Cipher(ESK) || Cipher(TAKpriv)格式組裝結果數據,再進行Base64編碼。
cat CipherEsk.bin CipherTakPriv.bin > EncryptedKeyMaterial.bin openssl enc -e -base64 -A -in EncryptedKeyMaterial.bin -out EncryptedKeyMaterial_base64.txt
說明EncryptedKeyMaterial_base64.txt即為可導入KMS的密鑰材料文件。
步驟四:導入密鑰材料
在密鑰詳情頁面,單擊導入密鑰材料,在導入打包后的密鑰材料對話框,完成各項配置后,單擊確定。
配置項 | 說明 |
打包后的密鑰材料 | 上傳步驟三:使用包裝公鑰加密密鑰材料中生成的密鑰材料文件。 |
導入令牌 | 上傳步驟二:下載包裝公鑰和導入令牌中下載的令牌文件。 |
密鑰材料過期時間 | 支持選擇永不過期,也可以自定義過期時間。 重要 如果設置了密鑰材料過期時間,在設置的時間點之后KMS會刪除已過期的密鑰材料,您將無法使用該密鑰材料。如需恢復使用,可以為密鑰再次導入相同的密鑰材料。 |
導入密鑰材料成功后,密鑰狀態從待導入更新為啟用中。
通過SDK導入密鑰材料
您可以通過阿里云SDK在KMS中創建RSA、ECC和SM2算法的密鑰,并導入密鑰材料。Java代碼示例如下。
阿里云賬號AccessKey擁有所有OpenAPI的訪問權限,建議您使用RAM用戶進行API訪問或日常運維。強烈建議不要把AccessKey ID和AccessKey Secret保存到工程代碼里,否則可能導致AccessKey泄露,威脅您賬號下所有資源的安全。
本示例以將AccessKey配置在環境變量ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET的方式來實現身份驗證為例。
更多認證信息配置方式,請參見管理訪問憑據。
不同操作系統的環境變量配置方法不同,具體操作,請參見在Linux、macOS和Windows系統配置環境變量。
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource.PSpecified;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import com.aliyuncs.AcsRequest;
import com.aliyuncs.AcsResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.kms.model.v20160120.CreateKeyRequest;
import com.aliyuncs.kms.model.v20160120.CreateKeyResponse;
import com.aliyuncs.kms.model.v20160120.GetParametersForImportRequest;
import com.aliyuncs.kms.model.v20160120.GetParametersForImportResponse;
import com.aliyuncs.kms.model.v20160120.ImportKeyMaterialRequest;
import com.aliyuncs.kms.model.v20160120.ImportKeyMaterialResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class BringYourOwnAsymmetricKeySample {
static String regionId = "cn-hangzhou";
static String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
static String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
static String dedicatedKmsInstanceId = "*** Provide your DedicatedKmsInstanceId ***";
DefaultAcsClient kmsClient;
private final String SM2PKE_SM4_ECB = "SM2PKE_SM4_ECB";
private final String RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD = "RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD";
private static Provider BC = new BouncyCastleProvider();
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
static {
java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
public static void main(String[] args) {
//初始化KMS SDK。
DefaultAcsClient client = getClientForPublicEndpoint(regionId, accessKeyId, accessKeySecret);
BringYourOwnAsymmetricKeySample sample = new BringYourOwnAsymmetricKeySample(client);
//創建并導入EC_SM2類型的外部密鑰。
sample.doByok("EC_SM2", "EC_SM2", sample.SM2PKE_SM4_ECB, "SM4");
//創建并導入EC_P256類型的外部密鑰。
sample.doByok("EC_P256", "RSA_2048", sample.RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD, "AES_256");
//創建并導入RSA類型的外部密鑰。
sample.doByok("RSA_2048", "RSA_2048", sample.RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD, "AES_256");
}
public static DefaultAcsClient getClientForPublicEndpoint(String regionId, String accessKeyId, String accessKeySecret) {
/**
* Construct an Aliyun Client:
* Set RegionId, AccessKeyId and AccessKeySecret
*/
IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
return client;
}
public BringYourOwnAsymmetricKeySample(DefaultAcsClient kmsClient) {
this.kmsClient = kmsClient;
}
public void doByok(String targetKeySpec, String wrappingKeySpec, String wrappingAlgorithm, String ephemeralKeySpec) {
try {
//創建ECC外部密鑰。
CreateKeyResponse.KeyMetadata keyMetadata = this.createExternalKeyInDkms(dedicatedKmsInstanceId, targetKeySpec, "SIGN/VERIFY");
String keyId = keyMetadata.getKeyId();
//獲取導入密鑰材料。
GetParametersForImportResponse parametersForImportResponse = this.getParametersForImport(keyId, wrappingKeySpec,
wrappingAlgorithm);
String importToken = parametersForImportResponse.getImportToken();
String publicKeyBase64 = parametersForImportResponse.getPublicKey();
//生成瞬時對稱密鑰。
byte[] ephemeralSymmetricKeyPlaintext = this.generateEphemeralSymmetricKey(ephemeralKeySpec);
//生成目標非對稱密鑰。
byte[] targetAsymmetricKeyPlaintext = this.generateTargetAsymmetricKey(targetKeySpec);
//使用加密公鑰加密瞬時對稱密鑰。
byte[] ephemeralSymmetricKeyCipher = this.encryptEphemeralSymmetricKey(publicKeyBase64,
wrappingAlgorithm, ephemeralSymmetricKeyPlaintext);
//使用瞬時對稱密鑰加密目標非對稱密鑰。
byte[] targetAsymmetricKeyCipher = this.encryptTargetAsymmetricKey(ephemeralSymmetricKeyPlaintext, targetAsymmetricKeyPlaintext,
wrappingAlgorithm);
//生成密鑰材料。
byte[] encryptedKeyMaterial = new byte[ephemeralSymmetricKeyCipher.length + targetAsymmetricKeyCipher.length];
System.arraycopy(ephemeralSymmetricKeyCipher, 0, encryptedKeyMaterial, 0, ephemeralSymmetricKeyCipher.length);
System.arraycopy(targetAsymmetricKeyCipher, 0, encryptedKeyMaterial, ephemeralSymmetricKeyCipher.length, targetAsymmetricKeyCipher.length);
String encryptedKeyMaterialBase64 = DatatypeConverter.printBase64Binary(encryptedKeyMaterial);
//導入密鑰材料到KMS。
this.importKeyMaterial(keyId, encryptedKeyMaterialBase64, importToken, 0L);
} catch (Exception e) {
e.printStackTrace();
}
}
private GetParametersForImportResponse getParametersForImport(String keyId, String keySpec, String algorithm) throws Exception {
GetParametersForImportRequest request = new GetParametersForImportRequest();
request.setAcceptFormat(FormatType.JSON);
request.setMethod(MethodType.POST);
request.setProtocol(ProtocolType.HTTPS);
request.setKeyId(keyId);
request.setWrappingKeySpec(keySpec);
request.setWrappingAlgorithm(algorithm);
GetParametersForImportResponse resp;
try {
resp = this.getAcsResponseWithRetry(request);
} catch (Exception e) {
throw e;
}
return resp;
}
private CreateKeyResponse.KeyMetadata createExternalKeyInDkms(String dedicatedKmsInstance, String keySpec, String keyUsage) throws Exception {
CreateKeyRequest request = new CreateKeyRequest();
//創建外部密鑰。
request.setOrigin("EXTERNAL");
request.setKeyStoreId(dedicatedKmsInstance);
request.setKeySpec(keySpec);
request.setKeyUsage(keyUsage);
request.setProtocol(ProtocolType.HTTPS);
request.setAcceptFormat(FormatType.JSON);
request.setMethod(MethodType.POST);
CreateKeyResponse.KeyMetadata ret = null;
String requestId = null;
try {
CreateKeyResponse response = getAcsResponseWithRetry(request);
ret = response.getKeyMetadata();
requestId = response.getRequestId();
} catch (Exception e) {
throw e;
}
return Pair.of(ret, requestId).getKey();
}
private <T extends AcsResponse> T getAcsResponseWithRetry(AcsRequest<T> request) throws ServerException,
ClientException {
String expStr = "Retry Max Times";
for (int i = 0; i < 3; i++) {
try {
T resp = this.kmsClient.getAcsResponse(request);
if (resp == null) {
throw new ClientException("Get a null response");
}
return resp;
} catch (ServerException e) {
throw e;
} catch (ClientException e) {
expStr = e.toString();
//need retry
if (expStr.contains("SDK.ServerUnreachable")) {
continue;
}
throw e;
}
}
throw new ClientException(expStr);
}
private byte[] generateEphemeralSymmetricKey(String ephemeralSymmetricKeySpec) throws Exception {
//瞬時對稱密鑰是AES_256時,長度為32比特。
int ephemeralSymmetricKeyLength = 32;
if ("SM4".equals(ephemeralSymmetricKeySpec)) {
ephemeralSymmetricKeyLength = 16;
}
byte[] key = new byte[32];
new Random().nextBytes(key);
return key;
}
private byte[] generateTargetAsymmetricKey(String keySpec) throws Exception {
PrivateKey privateKey = null;
//生成SM2密鑰,并獲取私鑰的D值。
if ("EC_SM2".equals(keySpec)) {
ECPrivateKey ecPrivateKey = (ECPrivateKey)generateSm2KeyPair().getPrivate();
byte[] dT = ecPrivateKey.getS().toByteArray();
byte[] d = new byte[32];
if (dT.length == 33) {
System.arraycopy(dT, 1, d, 0, 32);
}
return dT.length == 32 ? dT : d;
}
//生成RSA或者ECC私鑰。
if (keySpec.contains("RSA")) {
String[] keySpecAttrs = keySpec.split("_");
int bits = Integer.parseInt(keySpecAttrs[keySpecAttrs.length - 1]);
privateKey = generateRsaKeyPair(bits).getPrivate();
} else if (keySpec.contains("EC")) {
if (keySpec.contains("P256K")) {
//生成EC_P256K私鑰。
privateKey = generateEccKeyPair("secp256k1").getPrivate();
} else {
//生成EC_P256私鑰。
privateKey= generateEccKeyPair("secp256r1").getPrivate();
}
}
if (privateKey != null) {
//返回PKCS#8格式的私鑰。
return privateKey.getEncoded();
}
return null;
}
private KeyPair generateEccKeyPair(String keySpec)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
ECGenParameterSpec ecSpec = new ECGenParameterSpec(keySpec);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(ecSpec, new SecureRandom());
return keyPairGenerator.generateKeyPair();
}
private KeyPair generateRsaKeyPair(int length) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(length);
return keyGen.genKeyPair();
}
private KeyPair generateSm2KeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
keyGen.initialize(new ECGenParameterSpec("sm2p256v1"), new SecureRandom());
return keyGen.genKeyPair();
}
private byte[] encryptEphemeralSymmetricKey (String publicKeyBase64, String wrappingAlgorithm, byte[] ephemeralSymmetricKeyPlaintext) throws Exception {
PublicKey publickey = null;
byte[] enchbk = null;
if ("RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD".equals(wrappingAlgorithm)) {
publickey = parseDerPublicKey("RSA", publicKeyBase64);
Cipher oaepFromAlgo = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSpecified.DEFAULT);
oaepFromAlgo.init(Cipher.ENCRYPT_MODE, publickey, oaepParams);
enchbk = oaepFromAlgo.doFinal(ephemeralSymmetricKeyPlaintext);
} else if ("SM2PKE_SM4_ECB".equals(wrappingAlgorithm)) {
publickey = parseDerPublicKey("EC", publicKeyBase64, BC);
BCECPublicKey localECPublicKey = (BCECPublicKey) publickey;
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), ecDomainParameters);
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters));
enchbk = sm2Engine.processBlock(ephemeralSymmetricKeyPlaintext, 0, ephemeralSymmetricKeyPlaintext.length);
} else {
throw new Exception("Invalid wrappingAlgorithm");
}
return enchbk;
}
private PublicKey parseDerPublicKey(String keyType, String pemKey) throws Exception {
byte[] derKey = DatatypeConverter.parseBase64Binary(pemKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
return KeyFactory.getInstance(keyType).generatePublic(keySpec);
}
private PublicKey parseDerPublicKey(String keyType, String pemKey, Provider provider) throws Exception {
byte[] derKey = DatatypeConverter.parseBase64Binary(pemKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
return KeyFactory.getInstance(keyType, provider).generatePublic(keySpec);
}
private byte[] encryptTargetAsymmetricKey (byte[] secretKey, byte[] targetAsymmetricKeyPlaintext, String wrappingAlgorithm)
throws Exception {
if ("RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD".equals(wrappingAlgorithm)) {
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(targetAsymmetricKeyPlaintext);
} else if ("SM2PKE_SM4_ECB".equals(wrappingAlgorithm)) {
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "SM4");
Cipher cipher = Cipher.getInstance("SM4/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(targetAsymmetricKeyPlaintext);
}
throw new Exception("Invalid WrappingAlgorithm");
}
private boolean importKeyMaterial(
String keyId,
String material,
String token,
Long expire
) throws Exception {
ImportKeyMaterialRequest req = newImportKeyMaterialRequest(
keyId, material, token, expire);
try {
ImportKeyMaterialResponse resp = this.getAcsResponseWithRetry(req);
} catch (Exception e) {
throw e;
}
return true;
}
private ImportKeyMaterialRequest newImportKeyMaterialRequest(
String keyId,
String material,
String token,
Long expire
) {
ImportKeyMaterialRequest request = new ImportKeyMaterialRequest();
request.setAcceptFormat(FormatType.JSON);
request.setMethod(MethodType.POST);
request.setProtocol(ProtocolType.HTTPS);
request.setEncryptedKeyMaterial(material);
request.setImportToken(token);
request.setKeyId(keyId);
request.setKeyMaterialExpireUnix(expire);
return request;
}
}
常見問題
是否支持刪除密鑰材料?
支持刪除。
導入的密鑰材料過期或者被刪除后,其密鑰將無法使用,需要再次導入相同的密鑰材料才可正常使用。
直接刪除密鑰材料
控制臺:在密鑰詳情頁的密鑰材料區域,單擊刪除密鑰材料。
API接口:調用DeleteKeyMaterial接口刪除,該操作不會刪除您的密鑰。
過期后由KMS刪除
在導入密鑰材料時設置過期時間,在時間點之后KMS會刪除已過期的密鑰材料。
如何重新導入相同的密鑰材料?
密鑰材料過期或刪除后,您可以再次導入相同的密鑰材料,密鑰才可繼續使用。
刪除過期的密鑰材料。
在密鑰詳情頁,單擊密鑰材料頁簽,單擊刪除密鑰材料。
重新下載包裝公鑰和導入令牌。具體操作,請參見步驟二:下載包裝公鑰和導入令牌。
說明密鑰包裝過程不會影響密鑰材料的內容,因此,您可以使用不同的包裝公鑰和不同的包裝算法來導入相同的密鑰材料。
使用包裝公鑰加密密鑰材料。具體操作,請參見步驟三:使用包裝公鑰加密密鑰材料。
說明密鑰材料必須與之前過期的密鑰材料為同一個。
使用導入令牌,導入加密后的密鑰材料。具體操作,請參見步驟四:導入密鑰材料。
如何判斷密鑰材料是由外部導入還是由KMS生成?
方式一:在密鑰管理服務控制臺查看。
在密鑰管理頁面,單擊用戶主密鑰頁簽,選擇實例ID后定位到目標密鑰,單擊操作列的詳情,在詳情頁面查看密鑰材料來源。
方式二:通過調用DescribeKey接口查看。
如果
Origin
值為EXTERNAL
,說明密鑰材料由外部導入。如果Origin
值為Aliyun_KMS
,說明密鑰材料由KMS生成。
如何輪轉使用外部密鑰材料的密鑰?
對導入外部密鑰材料的密鑰,KMS不提供定期自動輪轉功能。如果您需要輪轉,只能創建一個新的密鑰然后導入新的密鑰材料。