Skip to content

Commit

Permalink
Support key less certificates in Key Vault. (Azure#23002)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhichengliu12581 authored Jul 30, 2021
1 parent 75e17d8 commit c859ce8
Show file tree
Hide file tree
Showing 24 changed files with 1,053 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ the main ServiceBusClientBuilder. -->
<!-- suppress the runtime exception in the KeyVaultClient class-->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.jca.implementation.KeyVaultClient.java"/>
<!-- suppress the runtime exception in the KeyVaultKeyLessECSignature class-->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.jca.implementation.signature.KeyVaultKeyLessECSignature.java"/>
<!-- suppress the runtime exception in the KeyVaultKeyLessRsaSignature class-->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.jca.implementation.signature.KeyVaultKeyLessRsaSignature.java"/>
<!-- suppress the runtime exception in the AbstractKeyVaultKeyLessSignature class-->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.jca.implementation.signature.AbstractKeyVaultKeyLessSignature.java"/>

<!-- This class overrides a method that throws, so it cannot be avoided. -->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLoggerCheck" files="com.azure.security.keyvault.jca.KeyVaultTrustManager"/>
Expand Down Expand Up @@ -605,7 +614,7 @@ the main ServiceBusClientBuilder. -->
<!-- Avoiding PagedFlux check if already using RetriableDownloadFlux. -->
<!-- Issue: https://github.com/Azure/azure-sdk-for-java/issues/22117 -->
<suppress checks="com.azure.tools.checkstyle.checks.ServiceClientCheck" files="com.azure.communication.callingserver.CallingServerAsyncClient"/>

<!-- Third party source code files, retained as is. -->
<suppress checks="[a-zA-Z0-9]*" files="com.azure.identity.implementation.intellij.Aes"/>
<suppress checks="[a-zA-Z0-9]*" files="com.azure.identity.implementation.intellij.HashedBlockInputStream"/>
Expand Down
3 changes: 2 additions & 1 deletion sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Release History

## 2.0.0-beta.1 (Unreleased)

### New Features
- Support key less certificate. ([#22105](https://github.com/Azure/azure-sdk-for-java/issues/22105))

## 1.0.1 (2021-07-01)
### Bug Fixes
Expand Down
36 changes: 36 additions & 0 deletions sdk/keyvault/azure-security-keyvault-jca/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,41 @@ azure:
custom: # The file location where you store the custom certificate
```
### Key-Less certificates
You can set the private key as [non-exportable] to ensure the security of the key.
Note if you want to use key less certificate, you must add `sign` permission.

You can add permission in portal: ![Sign To Principal](resources/SignToPrincipal.png)

Or add permission by cli command:
```shell
az keyvault set-policy --name ${KEY_VAULT} \
--object-id ${MANAGED_IDENTITY} \
--key-permissions get list sign\
--secret-permissions get list \
--certificate-permissions get list
```
Please replace `${KEY_VAULT}` with your key vault name and replace `${MANAGED_IDENTITY}` with your principal's object-id.

### Supported key type
Content Type | Key Type | Key Size or Elliptic curve name | Sign algorithm | Support |
-------------|----------|---------------------------------|---------------- |-------- |
PKCS #12 | RSA | 2048 | RSASSA-PSS | ✔ |
PKCS #12 | RSA | 3072 | RSASSA-PSS | ✔ |
PKCS #12 | RSA | 4096 | RSASSA-PSS | ✔ |
PKCS #12 | EC | P-256 | SHA256withECDSA | ✔ |
PKCS #12 | EC | P-384 | SHA384withECDSA | ✔ |
PKCS #12 | EC | P-521 | SHA512withECDSA | ✔ |
PKCS #12 | EC | P-256K | | ✘ |
PEM | RSA | 2048 | RSASSA-PSS | ✔ |
PEM | RSA | 3072 | RSASSA-PSS | ✔ |
PEM | RSA | 4096 | RSASSA-PSS | ✔ |
PEM | EC | P-256 | SHA256withECDSA | ✔ |
PEM | EC | P-384 | SHA384withECDSA | ✔ |
PEM | EC | P-521 | SHA512withECDSA | ✔ |
PEM | EC | P-256K | | ✘ |

## Troubleshooting
### General
Azure Key Vault JCA clients raise exceptions. For example, if you try to check a client's identity with a certificate chain that does not include a trusted certificate, a `CertificateException` will be thrown. In the following snippet, the error is handled gracefully by catching the exception and displaying additional information about the error.
Expand Down Expand Up @@ -167,5 +202,6 @@ This project has adopted the [Microsoft Open Source Code of Conduct][microsoft_c
[spring_boot_starter]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/spring/azure-spring-boot-starter-keyvault-certificates/README.md
[jca_reference_guide]: https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html
[microsoft_code_of_conduct]: https://opensource.microsoft.com/codeofconduct/
[non-exportable]: https://docs.microsoft.com/azure/key-vault/certificates/about-certificates#exportable-or-non-exportable-key

![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-java%2Fsdk%2Fkeyvault%2Fazure-security-keyvault-jca%2FREADME.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.jca;

import java.math.BigInteger;

/**
* encode signature
* Ref: https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-der-encoding-of-asn-1-types
*/
public class KeyVaultEncode {

private static final byte TAG_INTEGER = 0x02;
private static final byte TAG_SEQUENCE = 0x30;

/**
* Decode signatures imitating ECUtil
* @param signature signature get by keyvault
* @return decoded signatures
*/
public static byte[] encodeByte(byte[] signature) {
int halfLength = signature.length >> 1;
byte[] leftResult = toBigIntegerBytesWithLengthPrefix(signature, 0, halfLength);
byte[] rightResult = toBigIntegerBytesWithLengthPrefix(signature, halfLength, halfLength);
byte[] resultLengthBytes = buildLengthBytes(TAG_SEQUENCE, leftResult.length + rightResult.length);
return concatBytes(resultLengthBytes, leftResult, rightResult);
}

static byte[] toBigIntegerBytesWithLengthPrefix(byte[] bytes, int offset, int length) {
byte[] magnitude = new byte[length];
System.arraycopy(bytes, offset, magnitude, 0, length);
BigInteger bigInteger = new BigInteger(1, magnitude);
byte[] bigIntegerArray = bigInteger.toByteArray();
return concatBytes(buildLengthBytes(TAG_INTEGER, bigIntegerArray.length), bigIntegerArray);
}

static byte[] concatBytes(byte[] bytes1, byte[] bytes2) {
byte[] result = new byte[bytes1.length + bytes2.length];
System.arraycopy(bytes1, 0, result, 0, bytes1.length);
System.arraycopy(bytes2, 0, result, bytes1.length, bytes2.length);
return result;
}

static byte[] concatBytes(byte[] bytes1, byte[] bytes2, byte[] bytes3) {
byte[] result = new byte[bytes1.length + bytes2.length + bytes3.length];
System.arraycopy(bytes1, 0, result, 0, bytes1.length);
System.arraycopy(bytes2, 0, result, bytes1.length, bytes2.length);
System.arraycopy(bytes3, 0, result, bytes1.length + bytes2.length, bytes3.length);
return result;
}

static byte[] buildLengthBytes(byte tag, int len) {
if (len < 128) {
return new byte[] {tag, ((byte) len)};
} else if (len < (1 << 8)) {
return new byte[] {tag, (byte) 0x081, (byte) len};
} else if (len < (1 << 16)) {
return new byte[] {tag, (byte) 0x082, (byte) (len >> 8), (byte) len};
} else if (len < (1 << 24)) {
return new byte[] {tag, (byte) 0x083, (byte) (len >> 16), (byte) (len >> 8), (byte) len};
} else {
return new byte[] {tag, (byte) 0x084, (byte) (len >> 24), (byte) (len >> 16), (byte) (len >> 8), (byte) len};
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@

package com.azure.security.keyvault.jca;

import com.azure.security.keyvault.jca.implementation.signature.KeyVaultKeyLessRsaSignature;
import com.azure.security.keyvault.jca.implementation.signature.KeyVaultKeyLessEcSha384Signature;
import com.azure.security.keyvault.jca.implementation.signature.KeyVaultKeyLessEcSha512Signature;
import com.azure.security.keyvault.jca.implementation.signature.KeyVaultKeyLessEcSha256Signature;
import com.azure.security.keyvault.jca.implementation.signature.AbstractKeyVaultKeyLessSignature;

import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;

/**
* The Azure Key Vault security provider.
Expand Down Expand Up @@ -86,7 +94,31 @@ private void initialize() {
null
)
);
Stream.of(
KeyVaultKeyLessRsaSignature.class,
KeyVaultKeyLessEcSha256Signature.class,
KeyVaultKeyLessEcSha384Signature.class,
KeyVaultKeyLessEcSha512Signature.class)
.forEach(c -> putService(
new Service(
this,
"Signature",
getAlgorithmName(c),
c.getName(),
null,
null
)
));
return null;
});
}


private String getAlgorithmName(Class<? extends AbstractKeyVaultKeyLessSignature> c) {
try {
return c.getDeclaredConstructor().newInstance().getAlgorithmName();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
return "";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.jca;

import java.security.PrivateKey;

/**
* KeyVault fake private which work when key less
*/
public class KeyVaultPrivateKey implements PrivateKey {

/**
* Stores the serial version UID.
*/
private static final long serialVersionUID = 30_10_00;

private String kid;

private String algorithm;

/**
* Builder for key vault private key
* @param algorithm algorithm
* @param kid The key id
*/
public KeyVaultPrivateKey(String algorithm, String kid) {
this.algorithm = algorithm;
this.kid = kid;
}

/**
* Get the KeyId
* @return the KeyId
*/
public String getKid() {
return kid;
}

/**
* Store the KeyId
* @param kid the KeyId
*/
public void setKid(String kid) {
this.kid = kid;
}

/**
* Store key vault certificate algorithm
* @param algorithm algorithm
*/
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}

@Override
public String getAlgorithm() {
return algorithm;
}

@Override
public String getFormat() {
return null;
}

@Override
public byte[] getEncoded() {
return new byte[0];
}
}
Loading

0 comments on commit c859ce8

Please sign in to comment.