Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.

Commit f2aefe3

Browse files
Fix for issues #79, #80, #81, #82, #83 and #84
1 parent a7d3623 commit f2aefe3

28 files changed

Lines changed: 1436 additions & 992 deletions

src/main/java/com/venafi/vcert/sdk/certificate/CertificateRequest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public class CertificateRequest {
7373
private int validityHours;
7474
private String issuerHint;
7575
private Collection<CustomField> customFields;
76+
private DataFormat dataFormat;
7677

7778
public CertificateRequest() {
7879
this.dnsNames = emptyList();
@@ -92,6 +93,10 @@ public ChainOption chainOption() {
9293
public PrivateKey privateKey() {
9394
return (!Objects.isNull(keyPair)) ? keyPair.getPrivate() : null;
9495
}
96+
97+
public DataFormat dataFormat() {
98+
return (!Objects.isNull(dataFormat)) ? dataFormat : DataFormat.PKCS8;
99+
}
95100

96101
public void generatePrivateKey() throws VCertException {
97102
if (keyPair != null) {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
*
3+
*/
4+
package com.venafi.vcert.sdk.certificate;
5+
6+
/**
7+
* @author Marcos E. Albornoz
8+
*
9+
*/
10+
public enum DataFormat {
11+
12+
LEGACY("LEGACY"), PKCS8("PKCS#8");
13+
14+
private String id;
15+
16+
DataFormat(String id) {
17+
this.id = id;
18+
}
19+
20+
@Override
21+
public String toString() {
22+
return id.toString();
23+
}
24+
25+
}

src/main/java/com/venafi/vcert/sdk/certificate/PEMCollection.java

Lines changed: 104 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,18 @@
3434
import org.bouncycastle.cert.X509CertificateHolder;
3535
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
3636
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
37+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
3738
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
3839
import org.bouncycastle.openssl.PEMEncryptor;
40+
import org.bouncycastle.openssl.PEMException;
3941
import org.bouncycastle.openssl.PEMKeyPair;
4042
import org.bouncycastle.openssl.PEMParser;
4143
import org.bouncycastle.openssl.bc.BcPEMDecryptorProvider;
4244
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
4345
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
46+
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
4447
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
48+
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
4549
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
4650
import org.bouncycastle.operator.InputDecryptorProvider;
4751
import org.bouncycastle.operator.OperatorCreationException;
@@ -55,6 +59,7 @@
5559
import org.bouncycastle.pkcs.jcajce.JcePKCS12MacCalculatorBuilder;
5660
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEOutputEncryptorBuilder;
5761
import org.bouncycastle.util.io.pem.PemObject;
62+
import org.bouncycastle.util.io.pem.PemObjectGenerator;
5863
import org.bouncycastle.util.io.pem.PemWriter;
5964
import lombok.Data;
6065
import com.venafi.vcert.sdk.VCertException;
@@ -79,35 +84,52 @@ public class PEMCollection {
7984
private PrivateKey privateKey;
8085
private String privateKeyPassword;
8186
private List<X509Certificate> chain = new ArrayList<>();
82-
87+
private DataFormat dataFormat;
88+
89+
/**
90+
* @deprecated Use {@link #fromStringPEMCollection(String, ChainOption, PrivateKey, String) } instead of it.
91+
* @param body
92+
* @param chainOption
93+
* @param privateKey
94+
* @param privateKeyPassword
95+
* @return
96+
* @throws VCertException
97+
*/
8398
public static PEMCollection fromResponse(String body, ChainOption chainOption,
84-
PrivateKey privateKey, String privateKeyPassword) throws VCertException {
99+
PrivateKey privateKey, String privateKeyPassword) throws VCertException {
100+
return fromStringPEMCollection(body, chainOption, privateKey, privateKeyPassword);
101+
}
102+
103+
public static PEMCollection fromStringPEMCollection(String stringPemCollection, ChainOption chainOption,
104+
PrivateKey privateKey, String privateKeyPassword) throws VCertException {
105+
return fromStringPEMCollection(stringPemCollection, chainOption, privateKey, privateKeyPassword, DataFormat.PKCS8);
106+
}
107+
108+
public static PEMCollection fromStringPEMCollection(String stringPemCollection, ChainOption chainOption,
109+
PrivateKey privateKey, String privateKeyPassword, DataFormat dataFormat) throws VCertException {
85110
List<X509Certificate> chain = new ArrayList<>();
86111

87-
PEMParser pemParser = new PEMParser(new StringReader(body));
112+
//1. Extracting the Certificates and PrivateKey
113+
PEMParser pemParser = new PEMParser(new StringReader(stringPemCollection));
88114
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
89-
JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter();
90115
try {
91116
Object object = pemParser.readObject();
92117
while (object != null) {
93118
if (object instanceof X509CertificateHolder) {
94119
Certificate certificate =
95120
certificateConverter.getCertificate((X509CertificateHolder) object);
96121
chain.add((X509Certificate) certificate);
97-
} else if (object instanceof PEMKeyPair) {
98-
privateKey = keyConverter.getPrivateKey(((PEMKeyPair) object).getPrivateKeyInfo());
99-
} else if (object instanceof PEMEncryptedKeyPair) {
100-
PEMKeyPair keyPair = ((PEMEncryptedKeyPair) object).decryptKeyPair(
101-
new BcPEMDecryptorProvider(privateKeyPassword.toCharArray()));
102-
privateKey = keyConverter.getPrivateKey(keyPair.getPrivateKeyInfo());
122+
} else {
123+
privateKey = parsePrivateKey(object, privateKeyPassword);
103124
}
104125

105126
object = pemParser.readObject();
106127
}
107-
} catch (IOException | CertificateException e) {
128+
} catch (IOException | CertificateException | PKCSException | OperatorCreationException e) {
108129
throw new VCertException("Unable to parse certificate from response", e);
109130
}
110131

132+
//2. Ordering the Certificates chain
111133
PEMCollection pemCollection;
112134
if (chain.size() > 0) {
113135
switch (chainOption) {
@@ -135,6 +157,7 @@ public static PEMCollection fromResponse(String body, ChainOption chainOption,
135157
}
136158
pemCollection.privateKey(privateKey);
137159
pemCollection.privateKeyPassword(privateKeyPassword);
160+
pemCollection.dataFormat(dataFormat);
138161

139162
return pemCollection;
140163
}
@@ -162,20 +185,38 @@ public String pemPrivateKey() {
162185

163186
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
164187
try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStream))) {
165-
PEMEncryptor encryptor = null;
166-
167-
if (privateKeyPassword != null) {
168-
encryptor = new JcePEMEncryptorBuilder(BOUNCY_CASTLE_ENCRYPTION_ALGORITHM)
169-
.build(privateKeyPassword.toCharArray());
170-
}
171-
172-
JcaMiscPEMGenerator gen = new JcaMiscPEMGenerator(this.privateKey, encryptor);
173-
pemWriter.writeObject(gen.generate());
174-
} catch (IOException e) {
188+
pemWriter.writeObject(getPemObjectGenerator(privateKey, privateKeyPassword));
189+
} catch (IOException | OperatorCreationException e) {
175190
throw new RuntimeException(e);
176191
}
177192
return new String(outputStream.toByteArray());
178193
}
194+
195+
private PemObjectGenerator getPemObjectGenerator(PrivateKey privateKey, String password) throws OperatorCreationException, IOException {
196+
197+
boolean toEncrypt = (privateKeyPassword != null && privateKeyPassword.length() > 0);
198+
199+
if (dataFormat == DataFormat.PKCS8) {
200+
OutputEncryptor encryptor = null;
201+
if (toEncrypt) {
202+
encryptor = new JceOpenSSLPKCS8EncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC)
203+
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
204+
.setRandom(new SecureRandom())
205+
.setPassword(privateKeyPassword.toCharArray())
206+
.build();
207+
}
208+
209+
return new JcaPKCS8Generator(privateKey, encryptor);
210+
} else {
211+
PEMEncryptor encryptor = null;
212+
if (toEncrypt) {
213+
encryptor = new JcePEMEncryptorBuilder(BOUNCY_CASTLE_ENCRYPTION_ALGORITHM)
214+
.build(privateKeyPassword.toCharArray());
215+
}
216+
217+
return new JcaMiscPEMGenerator(this.privateKey, encryptor);
218+
}
219+
}
179220

180221
public String pemCertificateChain() {
181222
StringBuilder pem = new StringBuilder();
@@ -335,11 +376,53 @@ public static SecretKeySpec passwordToCipherSecretKey(char[] password, byte[] iv
335376

336377
public static PrivateKey decryptPKCS8PrivateKey(PEMParser pemParser, String keyPassword) throws IOException, OperatorCreationException, PKCSException{
337378
PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemParser.readObject();
379+
return decryptPKCS8PrivateKey(encryptedPrivateKeyInfo, keyPassword);
380+
}
381+
382+
public static PrivateKey decryptPKCS8PrivateKey(PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo, String keyPassword) throws PEMException, OperatorCreationException, PKCSException{
338383
InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword.toCharArray());
339384
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
340385
PrivateKeyInfo decryptedPrivateKeyInfo = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(pkcs8Prov);
341386
return converter.getPrivateKey(decryptedPrivateKeyInfo);
342387
}
388+
389+
/**
390+
* This method returns the PrivateKey from the Object passed as argument. which was read by {@link PEMParser#readObject()}
391+
* @param object The managed types are {@link org.bouncycastle.openssl.PEMKeyPair PEMKeyPair}
392+
* , {@link org.bouncycastle.openssl.PEMEncryptedKeyPair PEMEncryptedKeyPair}
393+
* and {@link org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo PKCS8EncryptedPrivateKeyInfo}. These kind of objects are returned
394+
* by {@link PEMParser#readObject()} when it reads a PEM PrivateKey.
395+
* @param privateKeyPassword Only required if the object is a wrapper of an encrypted private key
396+
* ({@link org.bouncycastle.openssl.PEMEncryptedKeyPair PEMEncryptedKeyPair} or {@link org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo PKCS8EncryptedPrivateKeyInfo})
397+
* @return returns the PrivateKey decrypted. Null if the object argument was not one of the expected types.
398+
* @throws IOException
399+
* @throws OperatorCreationException
400+
* @throws PKCSException
401+
*/
402+
private static PrivateKey parsePrivateKey(Object object, String privateKeyPassword) throws IOException, OperatorCreationException, PKCSException {
403+
PrivateKey privateKey = null;
404+
//If it's an RSA Private Key instance
405+
if (object instanceof PEMKeyPair) {
406+
privateKey = getPrivateKey((PEMKeyPair)object);
407+
} else {
408+
//If it's an Encrypted RSA/EC Private Key instance
409+
if (object instanceof PEMEncryptedKeyPair) {
410+
PEMKeyPair keyPair = ((PEMEncryptedKeyPair) object).decryptKeyPair(
411+
new BcPEMDecryptorProvider(privateKeyPassword.toCharArray()));
412+
privateKey = getPrivateKey(keyPair);
413+
} else {
414+
//If it's an Encrypted PKCS#8 Private Key instance
415+
if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
416+
privateKey = decryptPKCS8PrivateKey((PKCS8EncryptedPrivateKeyInfo)object, privateKeyPassword);
417+
}
418+
}
419+
}
420+
return privateKey;
421+
}
422+
423+
private static PrivateKey getPrivateKey(PEMKeyPair keyPair) throws PEMException {
424+
return new JcaPEMKeyConverter().getPrivateKey(keyPair.getPrivateKeyInfo());
425+
}
343426

344427
@Data
345428
public static class RawPrivateKey {

src/main/java/com/venafi/vcert/sdk/connectors/ServerPolicy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public Policy toPolicy() {
120120
if (keyPair.keyAlgorithm().locked()) {
121121
KeyType keyType = KeyType.from(keyPair.keyAlgorithm().value());
122122
AllowedKeyConfiguration key =
123-
new AllowedKeyConfiguration().keyType(keyType).keySizes(new ArrayList<>());
123+
new AllowedKeyConfiguration().keyType(keyType).keySizes(new ArrayList<Integer>()).keyCurves(new ArrayList<EllipticCurve>());
124124
if (KeyType.RSA.equals(keyType)) {
125125
if (keyPair.keySize().locked()) {
126126
for (Integer keySize : KeyType.allSupportedKeySizes()) {

src/main/java/com/venafi/vcert/sdk/connectors/cloud/Cloud.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,22 @@ Response retrieveCertificate(@Param("id") String id, @Param("apiKey") String api
110110
@RequestLine("POST /outagedetection/v1/certificates/{id}/keystore")
111111
Response retrieveKeystore(@Param("id") String id, KeystoreRequest keystoreRequest, @Param("apiKey") String apiKey);
112112

113+
static Cloud connect() {
114+
return connect((Config)null);
115+
}
116+
113117
static Cloud connect(String baseUrl) {
114-
return FeignUtils.client(Cloud.class,
115-
Config.builder().baseUrl(
116-
normalizeUrl(isNotBlank(baseUrl) ? baseUrl : "https://api.venafi.cloud")).build());
118+
return connect(Config.builder().baseUrl(baseUrl).build());
117119
}
118120

119121
static Cloud connect(Config config) {
120-
config.baseUrl(
121-
normalizeUrl(isNotBlank(config.baseUrl()) ? config.baseUrl() : "https://api.venafi.cloud"));
122122

123-
return FeignUtils.client(Cloud.class, config);
123+
if(config == null)
124+
config = Config.builder().build();
125+
126+
config.baseUrl(isNotBlank(config.baseUrl()) ? normalizeUrl(config.baseUrl()) : "https://api.venafi.cloud/");
127+
128+
return FeignUtils.client(Cloud.class, config);
124129
}
125130

126131
static String normalizeUrl(String url) {

src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnector.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,13 +330,12 @@ private String getCertificateIdFromPickupId(CertificateRequest request) throws V
330330
try {
331331
TimeUnit.SECONDS.sleep(2);
332332
} catch (InterruptedException e) {
333-
e.printStackTrace();
334333
throw new AttemptToRetryException(e);
335334
}
336335
}
337336

338337
if (user == null || user.company() == null) {
339-
throw new UserNotAuthenticatedException("Must be authenticated to retieve certificate");
338+
throw new UserNotAuthenticatedException("Must be authenticated to retrieve certificate");
340339
}
341340

342341
if(certificateStatus == null) {
@@ -403,7 +402,7 @@ private PEMCollection retrieveCertificateAsPemCollectionFromCSRProvided(Certific
403402
}
404403
}
405404

406-
return PEMCollection.fromResponse(
405+
return PEMCollection.fromStringPEMCollection(
407406
certificateAsPemString,
408407
request.chainOption(),
409408
request.privateKey(),
@@ -434,7 +433,7 @@ private PEMCollection retrieveCertificateAsPemCollectionFromCSRServiceGenerated(
434433
throw new VCertException(e);
435434
}
436435

437-
return CloudConnectorUtils.getPEMCollectionFromKeyStoreAsStream(keyStoreAsStream, request.chainOption(), request.keyPassword());
436+
return CloudConnectorUtils.getPEMCollectionFromKeyStoreAsStream(keyStoreAsStream, request.chainOption(), request.keyPassword(), request.dataFormat());
438437
}
439438

440439
/**

src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorUtils.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.venafi.vcert.sdk.VCertException;
44
import com.venafi.vcert.sdk.certificate.CertificateRequest;
55
import com.venafi.vcert.sdk.certificate.ChainOption;
6+
import com.venafi.vcert.sdk.certificate.DataFormat;
67
import com.venafi.vcert.sdk.certificate.PEMCollection;
78
import com.venafi.vcert.sdk.connectors.ConnectorException.PolicyMatchException;
89
import com.venafi.vcert.sdk.connectors.cloud.CloudConnector.CsrAttributes;
@@ -389,7 +390,7 @@ public static String getVaaSChainOption(ChainOption chainOption) {
389390
}
390391
}
391392

392-
public static PEMCollection getPEMCollectionFromKeyStoreAsStream(InputStream keyStoreAsInputStream, ChainOption chainOption, String keyPassword) throws VCertException {
393+
public static PEMCollection getPEMCollectionFromKeyStoreAsStream(InputStream keyStoreAsInputStream, ChainOption chainOption, String keyPassword, DataFormat dataFormat) throws VCertException {
393394
String certificateAsPem = null;
394395

395396
String pemFileSuffix = null;
@@ -406,22 +407,25 @@ public static PEMCollection getPEMCollectionFromKeyStoreAsStream(InputStream key
406407
while ((zipEntry = zis.getNextEntry())!= null) {
407408
String fileName = zipEntry.getName();
408409
if(fileName.endsWith(".key")) {
410+
//Getting the PrivateKey in PKCS8 and decrypting it
409411
PEMParser pemParser = new PEMParser(new InputStreamReader(zis));
410412
privateKey = PEMCollection.decryptPKCS8PrivateKey(pemParser, keyPassword);
411413
} else {
412-
if(fileName.endsWith(pemFileSuffix))
414+
if(fileName.endsWith(pemFileSuffix)) {
413415
certificateAsPem = new String(zis.readAllBytes());
416+
}
414417
}
415418
}
416419
} catch (Exception e) {
417420
throw new VCertException(e);
418421
}
419422

420-
return PEMCollection.fromResponse(
423+
return PEMCollection.fromStringPEMCollection(
421424
certificateAsPem,
422425
chainOption,
423426
privateKey,
424-
keyPassword);
427+
keyPassword,
428+
dataFormat);
425429
}
426430

427431
@Data

src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTppConnector.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public abstract class AbstractTppConnector {
3636
protected static final String MISSING_ACCESS_TOKEN_MESSAGE = FAILED_TO_AUTHENTICATE_MESSAGE + "missing access token";
3737
protected static final String TPP_ATTRIBUTE_MANAGEMENT_TYPE = "Management Type";
3838
protected static final String TPP_ATTRIBUTE_MANUAL_CSR = "Manual Csr";
39+
protected static final String LEGACY_DATA_FORMAT = "base64";
40+
protected static final String PKCS8_DATA_FORMAT = "base64 (PKCS #8)";
3941

4042
// TODO can be enum
4143
@SuppressWarnings("serial")

0 commit comments

Comments
 (0)