[ldap-sdk-commits] SF.net SVN: ldap-sdk:[1584] trunk
A Java-based LDAP API
Brought to you by:
dirmgr,
kennethleo
From: <di...@us...> - 2023-01-06 23:50:32
|
Revision: 1584 http://sourceforge.net/p/ldap-sdk/code/1584 Author: dirmgr Date: 2023-01-06 23:50:29 +0000 (Fri, 06 Jan 2023) Log Message: ----------- Updated cert data RCKSC to handle encrypted DER Updated the CertificateDataReplaceCertificateKeyStoreContent class to handle reading encrypted private keys in DER form. Modified Paths: -------------- trunk/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContent.java trunk/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContentTestCase.java Modified: trunk/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContent.java =================================================================== --- trunk/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContent.java 2023-01-06 20:32:32 UTC (rev 1583) +++ trunk/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContent.java 2023-01-06 23:50:29 UTC (rev 1584) @@ -63,6 +63,7 @@ import com.unboundid.util.ThreadSafetyLevel; import com.unboundid.util.Validator; import com.unboundid.util.ssl.cert.CertException; +import com.unboundid.util.ssl.cert.PKCS8EncryptionHandler; import com.unboundid.util.ssl.cert.PKCS8PEMFileReader; import com.unboundid.util.ssl.cert.PKCS8PrivateKey; import com.unboundid.util.ssl.cert.X509Certificate; @@ -572,7 +573,7 @@ // If the first byte is 0x30, then that indicates it's a DER sequence. if (firstByte == 0x30) { - return readDERPrivateKey(file, bis); + return readDERPrivateKey(file, bis, encryptionPassword); } @@ -601,10 +602,13 @@ /** * Reads a DER-formatted PKCS #8 private key from the provided input stream. * - * @param file The file with which the provided input stream is - * associated. It must not be {@code null}. - * @param inputStream The input stream from which the private key will be - * read. It must not be {@code null}. + * @param file The file with which the provided input stream + * is associated. It must not be {@code null}. + * @param inputStream The input stream from which the private key + * will be read. It must not be {@code null}. + * @param encryptionPassword The password to use to decrypt the encrypted + * private key. It may be {@code null} if the + * private key is not encrypted. * * @return The bytes that comprise the encoded PKCS #8 private key. * @@ -614,7 +618,8 @@ @NotNull() private static byte[] readDERPrivateKey( @NotNull final File file, - @NotNull final InputStream inputStream) + @NotNull final InputStream inputStream, + @Nullable final char[] encryptionPassword) throws LDAPException { try (ASN1StreamReader asn1Reader = new ASN1StreamReader(inputStream)) @@ -627,9 +632,20 @@ file.getAbsolutePath())); } - return element.encode(); + final byte[] elementsBytes = element.encode(); + if (encryptionPassword == null) + { + return elementsBytes; + } + else + { + final PKCS8PrivateKey privateKey = + PKCS8EncryptionHandler.decryptPrivateKey(elementsBytes, + encryptionPassword); + return privateKey.getPKCS8PrivateKeyBytes(); + } } - catch (final IOException e) + catch (final Exception e) { Debug.debugException(e); Modified: trunk/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContentTestCase.java =================================================================== --- trunk/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContentTestCase.java 2023-01-06 20:32:32 UTC (rev 1583) +++ trunk/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/CertificateDataReplaceCertificateKeyStoreContentTestCase.java 2023-01-06 23:50:29 UTC (rev 1584) @@ -361,12 +361,13 @@ /** * Tests the behavior when trying to create certificate data from files - * that contain the DER representations of certificates and private keys. + * that contain the DER representations of certificates and (unencrypted) + * private keys. * * @throws Exception If an unexpected problem occurs. */ @Test() - public void testCreateFromDERFiles() + public void testCreateFromDERFilesWithUnencryptedPrivateKey() throws Exception { // Generate a self-signed Ca certificate. @@ -510,6 +511,171 @@ outputStream.write(caCertDER); } + assertEquals( + CertificateDataReplaceCertificateKeyStoreContent.readCertificateChain( + combinedCertFile).size(), + 2); + } + + + + /** + * Tests the behavior when trying to create certificate data from files + * that contain the DER representations of certificates and (encrypted) + * private keys. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testCreateFromDERFilesWithEncryptedPrivateKey() + throws Exception + { + // Generate a self-signed Ca certificate. + final String keyStorePath = getTestFilePath(); + final String caCertPath = getTestFilePath(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + assertEquals( + ManageCertificates.main(null, out, out, + "generate-self-signed-certificate", + "--keystore", keyStorePath, + "--keystore-password", "password", + "--alias", "ca-cert", + "--subject-dn", "CN=Example CA,O=Example Corp,C=US", + "--days-valid", "7300", + "--output-file", caCertPath, + "--output-format", "DER"), + ResultCode.SUCCESS, + StaticUtils.toUTF8String(out.toByteArray())); + + + // Generate a certificate signing request for a server certificate. + out.reset(); + final String serverCertRequestPath = getTestFilePath(); + assertEquals( + ManageCertificates.main(null, out, out, + "generate-certificate-signing-request", + "--keystore", keyStorePath, + "--keystore-password", "password", + "--alias", "server-cert", + "--subject-dn", "CN=ds.example.com,O=Example Corp,C=US", + "--output-file", serverCertRequestPath, + "--output-format", "DER"), + ResultCode.SUCCESS, + StaticUtils.toUTF8String(out.toByteArray())); + + + // Sign the certificate signing request. + out.reset(); + final String serverCertPath = getTestFilePath(); + assertEquals( + ManageCertificates.main(null, out, out, + "sign-certificate-signing-request", + "--keystore", keyStorePath, + "--keystore-password", "password", + "--signing-certificate-alias", "ca-cert", + "--request-input-file", serverCertRequestPath, + "--certificate-output-file", serverCertPath, + "--output-format", "DER", + "--days-valid", "365", + "--include-requested-extensions", + "--no-prompt"), + ResultCode.SUCCESS, + StaticUtils.toUTF8String(out.toByteArray())); + + + // Import the signed certificate chain into the key store. + out.reset(); + assertEquals( + ManageCertificates.main(null, out, out, + "import-certificate", + "--keystore", keyStorePath, + "--keystore-password", "password", + "--alias", "server-cert", + "--certificate-file", serverCertPath, + "--certificate-file", caCertPath, + "--no-prompt"), + ResultCode.SUCCESS, + StaticUtils.toUTF8String(out.toByteArray())); + + + // Export the private key for the server certificate. + out.reset(); + final String serverKeyPath = getTestFilePath(); + final File encryptionPasswordFile = + createTempFile("this-is-the-private-key-encryption-password"); + assertEquals( + ManageCertificates.main(null, out, out, + "export-private-key", + "--keystore", keyStorePath, + "--keystore-password", "password", + "--alias", "server-cert", + "--output-file", serverKeyPath, + "--output-format", "DER", + "--encryption-password-file", + encryptionPasswordFile.getAbsolutePath()), + ResultCode.SUCCESS, + StaticUtils.toUTF8String(out.toByteArray())); + + + // Make sure that we can create a certificate data object with the DER + // certificate chain and private key. + CertificateDataReplaceCertificateKeyStoreContent c = + new CertificateDataReplaceCertificateKeyStoreContent( + Arrays.asList( + new File(serverCertPath), + new File(caCertPath)), + new File(serverKeyPath), + encryptionPasswordFile); + + c = CertificateDataReplaceCertificateKeyStoreContent.decodeInternal( + c.encode()); + assertNotNull(c); + + assertNotNull(c.getCertificateChainData()); + assertEquals(c.getCertificateChainData().size(), 2); + + assertNotNull(c.getPrivateKeyData()); + + + // Make sure that we can create a certificate data object with the DER + // certificate chain without the private key. + c = new CertificateDataReplaceCertificateKeyStoreContent( + Arrays.asList( + new File(serverCertPath), + new File(caCertPath)), + NULL_FILE); + + c = CertificateDataReplaceCertificateKeyStoreContent.decodeInternal( + c.encode()); + assertNotNull(c); + + assertNotNull(c.getCertificateChainData()); + assertEquals(c.getCertificateChainData().size(), 2); + + assertNull(c.getPrivateKeyData()); + + + // Also cover the readCertificateChain method that takes an array of files. + assertEquals( + CertificateDataReplaceCertificateKeyStoreContent.readCertificateChain( + new File(serverCertPath), + new File(caCertPath)).size(), + 2); + + + // Concatenate the files together into a single file and verify that + // the readCertificateChain method will still get both certificates. + final byte[] serverCertDER = StaticUtils.readFileBytes(serverCertPath); + final byte[] caCertDER = StaticUtils.readFileBytes(caCertPath); + + final File combinedCertFile = createTempFile(); + assertTrue(combinedCertFile.delete()); + try (FileOutputStream outputStream = new FileOutputStream(combinedCertFile)) + { + outputStream.write(serverCertDER); + outputStream.write(caCertDER); + } + assertEquals( CertificateDataReplaceCertificateKeyStoreContent.readCertificateChain( combinedCertFile).size(), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |