3434import org .bouncycastle .cert .X509CertificateHolder ;
3535import org .bouncycastle .cert .jcajce .JcaX509CertificateConverter ;
3636import org .bouncycastle .cert .jcajce .JcaX509ExtensionUtils ;
37+ import org .bouncycastle .jce .provider .BouncyCastleProvider ;
3738import org .bouncycastle .openssl .PEMEncryptedKeyPair ;
3839import org .bouncycastle .openssl .PEMEncryptor ;
40+ import org .bouncycastle .openssl .PEMException ;
3941import org .bouncycastle .openssl .PEMKeyPair ;
4042import org .bouncycastle .openssl .PEMParser ;
4143import org .bouncycastle .openssl .bc .BcPEMDecryptorProvider ;
4244import org .bouncycastle .openssl .jcajce .JcaMiscPEMGenerator ;
4345import org .bouncycastle .openssl .jcajce .JcaPEMKeyConverter ;
46+ import org .bouncycastle .openssl .jcajce .JcaPKCS8Generator ;
4447import org .bouncycastle .openssl .jcajce .JceOpenSSLPKCS8DecryptorProviderBuilder ;
48+ import org .bouncycastle .openssl .jcajce .JceOpenSSLPKCS8EncryptorBuilder ;
4549import org .bouncycastle .openssl .jcajce .JcePEMEncryptorBuilder ;
4650import org .bouncycastle .operator .InputDecryptorProvider ;
4751import org .bouncycastle .operator .OperatorCreationException ;
5559import org .bouncycastle .pkcs .jcajce .JcePKCS12MacCalculatorBuilder ;
5660import org .bouncycastle .pkcs .jcajce .JcePKCSPBEOutputEncryptorBuilder ;
5761import org .bouncycastle .util .io .pem .PemObject ;
62+ import org .bouncycastle .util .io .pem .PemObjectGenerator ;
5863import org .bouncycastle .util .io .pem .PemWriter ;
5964import lombok .Data ;
6065import 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 {
0 commit comments