Skip to content

Commit c2dc73c

Browse files
authored
End to End TLS SSL step #8 - Add support for PEM based certificates (Azure#17019)
1 parent 12c9a67 commit c2dc73c

File tree

3 files changed

+84
-10
lines changed

3 files changed

+84
-10
lines changed

sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultClient.java

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,26 @@
88
import com.azure.security.keyvault.jca.model.CertificatePolicy;
99
import com.azure.security.keyvault.jca.model.KeyProperties;
1010
import com.azure.security.keyvault.jca.model.SecretBundle;
11+
import java.io.BufferedReader;
1112

1213
import java.io.ByteArrayInputStream;
1314
import java.io.IOException;
15+
import java.io.StringReader;
1416
import java.io.UnsupportedEncodingException;
1517
import java.net.URLEncoder;
1618
import java.security.Key;
19+
import java.security.KeyFactory;
1720
import java.security.KeyStore;
1821
import java.security.KeyStoreException;
1922
import java.security.NoSuchAlgorithmException;
23+
import java.security.PrivateKey;
2024
import java.security.UnrecoverableKeyException;
2125
import java.security.cert.Certificate;
2226
import java.security.cert.CertificateException;
2327
import java.security.cert.CertificateFactory;
2428
import java.security.cert.X509Certificate;
29+
import java.security.spec.InvalidKeySpecException;
30+
import java.security.spec.PKCS8EncodedKeySpec;
2531
import java.util.ArrayList;
2632
import java.util.Base64;
2733
import java.util.HashMap;
@@ -249,16 +255,25 @@ Key getKey(String alias, char[] password) {
249255
if (body != null) {
250256
JsonConverter converter = JsonConverterFactory.createJsonConverter();
251257
SecretBundle secretBundle = (SecretBundle) converter.fromJson(body, SecretBundle.class);
252-
try {
253-
KeyStore keyStore = KeyStore.getInstance("PKCS12");
254-
keyStore.load(
255-
new ByteArrayInputStream(Base64.getDecoder().decode(secretBundle.getValue())),
256-
"".toCharArray()
257-
);
258-
alias = keyStore.aliases().nextElement();
259-
key = keyStore.getKey(alias, "".toCharArray());
260-
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException ex) {
261-
LOGGER.log(WARNING, "Unable to decode key", ex);
258+
if (secretBundle.getContentType().equals("application/x-pkcs12")) {
259+
try {
260+
KeyStore keyStore = KeyStore.getInstance("PKCS12");
261+
keyStore.load(
262+
new ByteArrayInputStream(Base64.getDecoder().decode(secretBundle.getValue())),
263+
"".toCharArray()
264+
);
265+
alias = keyStore.aliases().nextElement();
266+
key = keyStore.getKey(alias, "".toCharArray());
267+
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException ex) {
268+
LOGGER.log(WARNING, "Unable to decode key", ex);
269+
}
270+
}
271+
if (secretBundle.getContentType().equals("application/x-pem-file")) {
272+
try {
273+
key = createPrivateKeyFromPem(secretBundle.getValue());
274+
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException ex) {
275+
LOGGER.log(WARNING, "Unable to decode key", ex);
276+
}
262277
}
263278
}
264279
}
@@ -271,4 +286,37 @@ Key getKey(String alias, char[] password) {
271286
LOGGER.exiting("KeyVaultClient", "getKey", key);
272287
return key;
273288
}
289+
290+
/**
291+
* Get the private key from the PEM string.
292+
*
293+
* @param pemString the PEM file in string format.
294+
* @return the private key
295+
* @throws IOException when an I/O error occurs.
296+
* @throws NoSuchAlgorithmException when algorithm is unavailable.
297+
* @throws InvalidKeySpecException when the private key cannot be generated.
298+
*/
299+
private PrivateKey createPrivateKeyFromPem(String pemString)
300+
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
301+
302+
StringBuilder builder = new StringBuilder();
303+
try (BufferedReader reader = new BufferedReader(new StringReader(pemString))) {
304+
String line = reader.readLine();
305+
if (line == null || !line.contains("BEGIN PRIVATE KEY")) {
306+
throw new IllegalArgumentException("No PRIVATE KEY found");
307+
}
308+
line = "";
309+
while (line != null) {
310+
if (line.contains("END PRIVATE KEY")) {
311+
break;
312+
}
313+
builder.append(line);
314+
line = reader.readLine();
315+
}
316+
}
317+
byte[] bytes = Base64.getDecoder().decode(builder.toString());
318+
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
319+
KeyFactory factory = KeyFactory.getInstance("RSA");
320+
return factory.generatePrivate(spec);
321+
}
274322
}

sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ public Key engineGetKey(String alias, char[] password) {
179179
key = keyVaultClient.getKey(alias, password);
180180
if (key != null) {
181181
certificateKeys.put(alias, key);
182+
if (aliases == null) {
183+
aliases = keyVaultClient.getAliases();
184+
}
182185
if (!aliases.contains(alias)) {
183186
aliases.add(alias);
184187
}

sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/model/SecretBundle.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,26 @@ public class SecretBundle implements Serializable {
1414
* Stores the serial version UID.
1515
*/
1616
private static final long serialVersionUID = 1L;
17+
18+
/**
19+
* Stores the content type.
20+
*/
21+
private String contentType;
1722

1823
/**
1924
* Stores the value.
2025
*/
2126
private String value;
2227

28+
/**
29+
* Get the content type.
30+
*
31+
* @return the content type.
32+
*/
33+
public String getContentType() {
34+
return contentType;
35+
}
36+
2337
/**
2438
* Get the value.
2539
*
@@ -28,6 +42,15 @@ public class SecretBundle implements Serializable {
2842
public String getValue() {
2943
return value;
3044
}
45+
46+
/**
47+
* Set the content type.
48+
*
49+
* @param contentType the content type.
50+
*/
51+
public void setContentType(String contentType) {
52+
this.contentType = contentType;
53+
}
3154

3255
/**
3356
* Set the value.

0 commit comments

Comments
 (0)