Skip to content

Commit

Permalink
Added support for TCPS and SEPS Wallet Providers for OCI Vault (#96)
Browse files Browse the repository at this point in the history
* Added support for TCPS and SEPS Wallet Providers for OCI Vault

* Added example-vault-wallet.properties for TCPS and SEPS wallet configurations && Renamed tls wallet property to oracle.jdbc.provider.tlsConfiguration

* Adopted 80-character line limit, fixed indentation, and replaced 'mutual TLS' with 'TLS' in documentation

* Added JavaDocs for default SEPS secrets and updated to use default secrets for username and password && Refactored to use Credentials inner class for storing username and password && Replaced missing mutual TLS (mTLS) with

* Added PemData.decode method

* Renamed PemDataTest

* Refactored TCPS Provider to Support Multiple File Types (SSO, PKCS12, PEM)

* Added TlsUtils

* fix(typo): Remove unused imports and Replace 'wallet' with 'file' and 'Password' with 'password'

* Enhance credential extraction to support single indexed connection string

* Update the README.md file

* Add more details about SEPS and TCPS wallets in readme file

---------

Co-authored-by: Michael McMahon <michael.a.mcmahon@oracle.com>
  • Loading branch information
MouhsinElmajdouby and Michael-A-McMahon authored Sep 27, 2024
1 parent 55e39af commit cc3f529
Show file tree
Hide file tree
Showing 17 changed files with 1,473 additions and 83 deletions.
4 changes: 4 additions & 0 deletions ojdbc-provider-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.database.security</groupId>
<artifactId>oraclepki</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,30 @@

package oracle.jdbc.provider.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.US_ASCII;

/**
Expand All @@ -58,6 +72,15 @@
*/
public final class PemData {

private static final Logger LOGGER =
Logger.getLogger(PemData.class.getName());

/**
* ASCII/UTF-8 encoded new line characters. PEM data decoders should
* recognize a carriage return followed by a newline as a new line.
*/
private static final byte[] ASCII_NEW_LINE = "\r\n".getBytes(US_ASCII);

/** Label that identifies the type of data */
private final Label label;

Expand All @@ -79,16 +102,38 @@ private PemData(Label label, byte[] base64Data) {
* @return A stream of PEM encoded data. Not null.
*/
public InputStream createInputStream() {

List<InputStream> inputStreams = Arrays.asList(
new ByteArrayInputStream(label.asciiBeginLabel),
new ByteArrayInputStream(ASCII_NEW_LINE),
new ByteArrayInputStream(label.beginTag.getBytes(US_ASCII)),
new ByteArrayInputStream(ASCII_NEW_LINE),
new ByteArrayInputStream(base64Data),
new ByteArrayInputStream(label.asciiEndLabel));
new ByteArrayInputStream(ASCII_NEW_LINE),
new ByteArrayInputStream(label.endTag.getBytes(US_ASCII)),
new ByteArrayInputStream(ASCII_NEW_LINE));

return new SequenceInputStream(
Collections.enumeration(inputStreams));
}

/**
* Returns the Label of this PEM data.
*
* @return The label for this data. Not null.
*/
public Label label() {
return label;
}

/**
* Returns the binary data encapsulated by this PemData. The data can be
* decoded according to the encoding specified for the {@link #label()}.
*
* @return Binary data. Not null.
*/
public byte[] data() {
return Base64.getMimeDecoder().decode(base64Data);
}

/**
* Returns PEM encoded data of a
* <a href="https://www.rfc-editor.org/rfc/rfc7468#section-10">
Expand All @@ -108,27 +153,201 @@ public static PemData encodePrivateKey(PrivateKey privateKey) {
}
}

private enum Label {
/**
* Decodes labeled data from ASCII encoded PEM text. Unrecognized labels are
* ignored.
*
* @param pemData Stream of ASCII encoded PEM text. Not null.
*
* @return PEM data for labels that were recognized in the stream. Not null.
*/
public static List<PemData> decode(InputStream pemData) {
List<PemData> pemDataList = new ArrayList<>();

String line;
BufferedReader reader =
new BufferedReader(new InputStreamReader(pemData, US_ASCII));
try {
while (null != (line = reader.readLine())) {

// Parse a BEGIN tag
String labelName = parseBeginLabelName(line);

// Ignore data outside of a BEGIN and END tags.
if (labelName == null)
continue;

// Check if the BEGIN tag has a recognized label
Label label = Label.NAME_TO_LABEL_MAP.get(labelName);

if (label == null) {
LOGGER.warning("Ignoring unrecognized PEM label: " + labelName);
// Ignore data for an unrecognized label
parseData(reader, null, toEndTag(labelName));
}
else {
// Capture data for a recognized label
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
parseData(reader, dataStream, label.endTag);
pemDataList.add(new PemData(label, dataStream.toByteArray()));
}
}

return pemDataList;
}
catch (Exception exception) {
throw new IllegalStateException(exception);
}
}

/**
* Parses the label name appearing in a BEGIN tag:
* <pre>
* -----BEGIN {label name}-----
* </pre>
*
* @param line Line to parse. Not null.
* @return The label name, or null if the line is not a BEGIN tag.
*/
private static String parseBeginLabelName(String line) {
line = line.trim();

if (!line.startsWith(Label.BEGIN_PREFIX))
return null;

if (!line.endsWith(Label.TAG_POSTFIX))
return null;

return line.substring(
Label.BEGIN_PREFIX.length(),
line.length() - Label.TAG_POSTFIX.length());
}

/**
* Returns an END tag for a label name:
* <pre>
* -----END {label name}-----
* </pre>
*
* @param labelName Label name appearing in the END tag. Not null.
* @return The END tag for the label name. Not null.
*/
private static String toEndTag(String labelName) {
return Label.END_PREFIX + labelName + Label.TAG_POSTFIX;
}

/**
* Parses data encapsulated by a BEGIN and END tag, starting with the next
* line of a reader, and stopping when a END tag is matched.
*
* @param reader Reader positioned on the next line of data to parse. Not
* null.
*
* @param outputStream Stream to output ASCII encoded data into. May be null
* if data should be ignored.
*
* @param endTag The END tag which terminates the data lines. Not null.
*
* @throws IOException If an error prevents the reader from reading lines,
* or an error prevents the output stream from writing.
*
* @throws IllegalStateException If no END tag is found.
*/
private static void parseData(
BufferedReader reader, OutputStream outputStream, String endTag)
throws IOException {

String line;
while (null != (line = reader.readLine())) {
// Ignore whitespace
line = line.trim();

if (endTag.equals(line))
break;

if (outputStream != null)
outputStream.write(line.getBytes(US_ASCII));
}

if (line == null)
throw missingEndTag(endTag);
}

/**
* Returns an exception indicating that an END tag is missing from a PEM
* data encoding.
*
* @param endTag The missing tag. Not null.
* @return Exception for the missing tag. Not null.
*/
private static IllegalStateException missingEndTag(String endTag) {
return new IllegalStateException("Missing END tag: " + endTag);
}

PRIVATE_KEY;
/**
* Labels recognized by {@link PemData}. These labels are defined in
* <a href="https://www.rfc-editor.org/rfc/rfc7468">
* RFC 7468
* </a>.
*/
public enum Label {

/**
* <a href="https://www.rfc-editor.org/rfc/rfc7468#section-10">
* Unencrypted PKCS #8 Private Key
* </a>
*/
PRIVATE_KEY("PRIVATE KEY"),

/**
* <a href="https://www.rfc-editor.org/rfc/rfc7468#section-11">
* Encrypted PKCS #8 Private Key
* </a>
*/
ENCRYPTED_PRIVATE_KEY("ENCRYPTED PRIVATE KEY"),

/**
* <a href="https://www.rfc-editor.org/rfc/rfc7468#section-5">
* Public-key certificate
* </a>
*/
CERTIFICATE("CERTIFICATE"),
;

/**
* The name of this label, derived from the name of this enum, with
* underscores replaced by spaces.
* Mapping of label names to {@link Label} members.
*/
private final String labelText = name().replace('_', ' ');
private static final Map<String, Label> NAME_TO_LABEL_MAP =
Arrays.stream(values())
.collect(Collectors.toMap(
label -> label.labelName,
Function.identity()));

/** The start of a BEGIN tag, up to the where the label name appears */
static final String BEGIN_PREFIX = "-----BEGIN ";

/** The start of an END tag, up to the where the label name appears */
static final String END_PREFIX = "-----END ";

/** The line that begins an encoding */
private final String beginLabel = format("\r\n-----BEGIN %s-----\r\n", labelText);
/** The end of a BEGIN or END tag, after where the label name appears */
static final String TAG_POSTFIX = "-----";

/** ASCII encoding of the line that begins an encoding */
private final byte[] asciiBeginLabel = beginLabel.getBytes(US_ASCII);
/**
* The name of which appears in BEGIN and END tags for this label.
*/
private final String labelName;

/** The line that ends an encoding */
private final String endLabel = format("\r\n-----END %s-----\r\n", labelText);
/** The tag that begins an encoding with the name of this label */
private final String beginTag;

/** ASCII encoding of the line that ends an encoding */
private final byte[] asciiEndLabel = endLabel.getBytes(US_ASCII);
/** The tag that ends an encoding with the name of this label */
private final String endTag;

Label(String labelName) {
this.labelName = labelName;
beginTag = BEGIN_PREFIX + labelName + TAG_POSTFIX;
endTag = toEndTag(labelName);
}

}

Expand Down
Loading

0 comments on commit cc3f529

Please sign in to comment.