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

Commit ab7ae63

Browse files
committed
Fixed private key missing from PEM collection when using local generation
1 parent edcf09c commit ab7ae63

5 files changed

Lines changed: 121 additions & 21 deletions

File tree

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

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import com.venafi.vcert.sdk.VCertException;
66
import lombok.Data;
77
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
8-
import org.bouncycastle.asn1.eac.ECDSAPublicKey;
98
import org.bouncycastle.asn1.x500.X500NameBuilder;
109
import org.bouncycastle.asn1.x500.style.BCStyle;
1110
import org.bouncycastle.jce.PKCS10CertificationRequest;
@@ -27,6 +26,7 @@
2726
import java.security.*;
2827
import java.security.cert.Certificate;
2928
import java.security.interfaces.RSAPublicKey;
29+
import java.security.interfaces.ECPublicKey;
3030
import java.security.spec.ECGenParameterSpec;
3131
import java.time.Duration;
3232
import java.util.Collection;
@@ -82,6 +82,12 @@ public ChainOption chainOption() {
8282
:ChainOption.ChainOptionRootFirst;
8383
}
8484

85+
public PrivateKey privateKey() {
86+
return (!Objects.isNull(keyPair))
87+
?keyPair.getPrivate()
88+
:null;
89+
}
90+
8591
public void generatePrivateKey() throws VCertException {
8692
if(keyPair != null) {
8793
return;
@@ -110,11 +116,9 @@ public void generateCSR() throws VCertException {
110116
for ( String san : dnsNames ) {
111117
sans.add(new GeneralName(GeneralName.dNSName, san));
112118
}
113-
114119
for ( InetAddress san : ipAddresses ) {
115120
sans.add(new GeneralName(GeneralName.iPAddress, new DEROctetString(san.getAddress())));
116121
}
117-
118122
for ( String san : emailAddresses ) {
119123
sans.add(new GeneralName(GeneralName.rfc822Name, san));
120124
}
@@ -232,8 +236,7 @@ public static class AttributeTypeAndValueSET {
232236
}
233237

234238
public boolean checkCertificate(Certificate certificate) throws VCertException {
235-
// TODO handle enum exception
236-
PublicKeyAlgorithm publicKeyAlgorithm = PublicKeyAlgorithm.valueOf(certificate.getPublicKey().getAlgorithm());
239+
PublicKeyAlgorithm publicKeyAlgorithm = KeyType.from(certificate.getPublicKey().getAlgorithm()).X509Type();
237240

238241
if(keyPair != null && keyPair.getPublic() != null && keyPair.getPrivate() != null) {
239242
if( keyType.X509Type() != publicKeyAlgorithm) {
@@ -249,11 +252,22 @@ public boolean checkCertificate(Certificate certificate) throws VCertException {
249252
}
250253
break;
251254
case ECDSA:
252-
ECDSAPublicKey certEcdsaPublicKey = (ECDSAPublicKey) certificate.getPublicKey();
253-
ECDSAPublicKey reqEcdsaPublicKey = (ECDSAPublicKey) keyPair.getPublic();
254-
// TODO make sure comparison is valid
255-
if(certEcdsaPublicKey.getPrimeModulusP().compareTo(reqEcdsaPublicKey.getPrimeModulusP()) != 0) {
256-
throw new VCertException("unmatched X for eliptic keys");
255+
ECPublicKey certEcPublicKey = (ECPublicKey) certificate.getPublicKey();
256+
ECPublicKey reqEcPublicKey = (ECPublicKey) keyPair.getPublic();
257+
258+
// https://stackoverflow.com/questions/24121801/how-to-verify-if-the-private-key-matches-with-the-certificate
259+
java.security.spec.ECParameterSpec certSpec = certEcPublicKey.getParams(), csrSpec = reqEcPublicKey.getParams();
260+
java.security.spec.EllipticCurve certCurve = certSpec.getCurve(), csrCurve = csrSpec.getCurve();
261+
java.security.spec.ECField certField = certCurve.getField(), csrField = csrCurve.getField();
262+
if ( certSpec != csrSpec //
263+
&& ( certSpec.getCofactor() != csrSpec.getCofactor() //
264+
|| ! certSpec.getOrder().equals( csrSpec.getOrder() ) //
265+
|| ! certSpec.getGenerator().equals( csrSpec.getGenerator() ) //
266+
|| certCurve != csrCurve //
267+
&& ( ! certCurve.getA().equals( csrCurve.getA() ) //
268+
|| ! certCurve.getB().equals( csrCurve.getB() ) //
269+
|| certField.getFieldSize() != csrField.getFieldSize() ) ) ) {
270+
throw new VCertException("unmatched parameters for elliptic keys");
257271
}
258272
break;
259273
default:
@@ -279,11 +293,22 @@ public boolean checkCertificate(Certificate certificate) throws VCertException {
279293
}
280294
break;
281295
case ECDSA:
282-
ECDSAPublicKey certEcdsaPublicKey = (ECDSAPublicKey) certificate.getPublicKey();
283-
ECDSAPublicKey reqEcdsaPublicKey = (ECDSAPublicKey) csr.getPublicKey();
284-
// TODO make sure comparison is valid
285-
if(certEcdsaPublicKey.getPrimeModulusP().compareTo(reqEcdsaPublicKey.getPrimeModulusP()) != 0) {
286-
throw new VCertException("unmatched X for eliptic keys");
296+
ECPublicKey certEcPublicKey = (ECPublicKey) certificate.getPublicKey();
297+
ECPublicKey reqEcPublicKey = (ECPublicKey) csr.getPublicKey();
298+
299+
// https://stackoverflow.com/questions/24121801/how-to-verify-if-the-private-key-matches-with-the-certificate
300+
java.security.spec.ECParameterSpec certSpec = certEcPublicKey.getParams(), csrSpec = reqEcPublicKey.getParams();
301+
java.security.spec.EllipticCurve certCurve = certSpec.getCurve(), csrCurve = csrSpec.getCurve();
302+
java.security.spec.ECField certField = certCurve.getField(), csrField = csrCurve.getField();
303+
if ( certSpec != csrSpec //
304+
&& ( certSpec.getCofactor() != csrSpec.getCofactor() //
305+
|| ! certSpec.getOrder().equals( csrSpec.getOrder() ) //
306+
|| ! certSpec.getGenerator().equals( csrSpec.getGenerator() ) //
307+
|| certCurve != csrCurve //
308+
&& ( ! certCurve.getA().equals( csrCurve.getA() ) //
309+
|| ! certCurve.getB().equals( csrCurve.getB() ) //
310+
|| certField.getFieldSize() != csrField.getFieldSize() ) ) ) {
311+
throw new VCertException("unmatched parameters for elliptic keys");
287312
}
288313
break;
289314
}

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

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
import org.bouncycastle.openssl.PEMKeyPair;
88
import org.bouncycastle.openssl.PEMParser;
99
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
10+
import org.bouncycastle.asn1.ASN1Encodable;
11+
import org.bouncycastle.asn1.ASN1Primitive;
12+
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
13+
import org.bouncycastle.util.io.pem.PemObject;
14+
import org.bouncycastle.util.io.pem.PemWriter;
1015

1116
import java.io.IOException;
1217
import java.io.StringReader;
@@ -15,16 +20,20 @@
1520
import java.security.cert.CertificateException;
1621
import java.util.ArrayList;
1722
import java.util.List;
23+
import java.util.Objects;
24+
import java.io.ByteArrayOutputStream;
25+
import java.io.OutputStreamWriter;
26+
import java.security.cert.CertificateEncodingException;
27+
1828

1929
@Data
2030
public class PEMCollection {
2131
private Certificate certificate;
2232
private PrivateKey privateKey;
2333
private List<Certificate> chain = new ArrayList<>();
2434

25-
public static PEMCollection fromResponse(String body, ChainOption chainOption) throws VCertException {
35+
public static PEMCollection fromResponse(String body, ChainOption chainOption, PrivateKey privateKey) throws VCertException {
2636
List<Certificate> chain = new ArrayList<>();
27-
PrivateKey privateKey = null;
2837

2938
PEMParser pemParser = new PEMParser(new StringReader(body));
3039
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
@@ -73,6 +82,10 @@ public static PEMCollection fromResponse(String body, ChainOption chainOption) t
7382
return pemCollection;
7483
}
7584

85+
public static PEMCollection fromResponse(String body, ChainOption chainOption) throws VCertException {
86+
return fromResponse(body, chainOption, null);
87+
}
88+
7689
// TODO deal with password? is it required?
7790
public static PEMCollection newPemCollection(Certificate certificate, PrivateKey privateKey, byte[] privateKeyPassword) {
7891
PEMCollection pemCollection = new PEMCollection();
@@ -82,4 +95,67 @@ public static PEMCollection newPemCollection(Certificate certificate, PrivateKey
8295
}
8396
return pemCollection;
8497
}
98+
99+
public String pemCertificate() {
100+
String pem = null;
101+
if (!Objects.isNull(this.certificate)) {
102+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
103+
try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStream))) {
104+
pemWriter.writeObject(new PemObject("CERTIFICATE", this.certificate.getEncoded()));
105+
} catch (CertificateEncodingException e) {
106+
throw new RuntimeException(e);
107+
} catch (IOException e) {
108+
throw new RuntimeException(e);
109+
}
110+
pem = new String(outputStream.toByteArray());
111+
}
112+
return pem;
113+
}
114+
115+
public String pemPrivateKey() {
116+
String pem = null;
117+
if (!Objects.isNull(this.privateKey)) {
118+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
119+
switch(KeyType.from(this.privateKey.getAlgorithm())) {
120+
case RSA:
121+
try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStream))) {
122+
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(this.privateKey.getEncoded());
123+
ASN1Encodable privateKeyPKCS1ASN1Encodable = pkInfo.parsePrivateKey();
124+
ASN1Primitive privateKeyPKCS1ASN1 = privateKeyPKCS1ASN1Encodable.toASN1Primitive();
125+
pemWriter.writeObject(new PemObject("RSA PRIVATE KEY", privateKeyPKCS1ASN1.getEncoded()));
126+
} catch (IOException e) {
127+
throw new RuntimeException(e);
128+
}
129+
pem = new String(outputStream.toByteArray());
130+
break;
131+
case ECDSA:
132+
try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStream))) {
133+
pemWriter.writeObject(new PemObject("EC PRIVATE KEY", this.privateKey.getEncoded()));
134+
} catch (IOException e) {
135+
throw new RuntimeException(e);
136+
}
137+
pem = new String(outputStream.toByteArray());
138+
break;
139+
}
140+
}
141+
return pem;
142+
}
143+
144+
public String pemCertificateChain() {
145+
StringBuilder pem = new StringBuilder();
146+
if (!Objects.isNull(this.chain)) {
147+
for(Certificate cert : this.chain) {
148+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
149+
try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStream))) {
150+
pemWriter.writeObject(new PemObject("CERTIFICATE", cert.getEncoded()));
151+
} catch (CertificateEncodingException e) {
152+
throw new RuntimeException(e);
153+
} catch (IOException e) {
154+
throw new RuntimeException(e);
155+
}
156+
pem.append(new String(outputStream.toByteArray()));
157+
}
158+
}
159+
return pem.toString();
160+
}
85161
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@ public enum PublicKeyAlgorithm {
44
Unknown,
55
RSA,
66
DSA,
7-
ECDSA,
8-
EC
7+
ECDSA
98
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public PEMCollection retrieveCertificate(CertificateRequest request) throws VCer
213213
break;
214214
}
215215
String body = certificateViaCSR(request.pickupId(), chainOption);
216-
PEMCollection pemCollection = PEMCollection.fromResponse(body, request.chainOption());
216+
PEMCollection pemCollection = PEMCollection.fromResponse(body, request.chainOption(), request.privateKey());
217217
request.checkCertificate(pemCollection.certificate());
218218
return pemCollection;
219219
} else {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public PEMCollection retrieveCertificate(CertificateRequest request) throws VCer
257257
if(isNotBlank(retrieveResponse.certificateData())) {
258258
PEMCollection pemCollection = PEMCollection.fromResponse(
259259
org.bouncycastle.util.Strings.fromByteArray(Base64.getDecoder().decode(retrieveResponse.certificateData())),
260-
request.chainOption());
260+
request.chainOption(), request.privateKey());
261261
request.checkCertificate(pemCollection.certificate());
262262
return pemCollection;
263263
}

0 commit comments

Comments
 (0)