com.rt.storage.api.client.util.PemReader Maven / Gradle / Ivy
package com.rt.storage.api.client.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* {@link Beta}
* PEM file reader.
*
* Supports reading any PEM stream that contains Base64 encoded content stored inside {@code
* "-----BEGIN ...-----"} and {@code "-----END ...-----"} tags. Each call to {@link
* #readNextSection()} parses the next section in the PEM file. If you need a section of a certain
* title use {@link #readNextSection(String)}, for example {@code readNextSection("PRIVATE KEY")}.
* To ensure that the stream is closed properly, call {@link #close()} in a finally block.
*
*
As a convenience, use {@link #readFirstSectionAndClose(Reader)} or {@link
* #readFirstSectionAndClose(Reader, String)} for the common case of only a single section in a PEM
* file (or only a single section of a given title).
*
*
Limitations:
*
*
*
*
* - Assumes the PEM file section content is not encrypted and cannot handle the case of any
* headers inside the BEGIN and END tag.
*
- It also ignores any attributes associated with any PEM file section.
*
*
* @since 1.14
* @author Yaniv Inbar
*/
@Beta
public final class PemReader {
private static final Pattern BEGIN_PATTERN = Pattern.compile("-----BEGIN ([A-Z ]+)-----");
private static final Pattern END_PATTERN = Pattern.compile("-----END ([A-Z ]+)-----");
/** Reader. */
private BufferedReader reader;
/** @param reader reader */
public PemReader(Reader reader) {
this.reader = new BufferedReader(reader);
}
/** Reads the next section in the PEM file or {@code null} for end of file. */
public Section readNextSection() throws IOException {
return readNextSection(null);
}
/**
* Reads the next section in the PEM file, optionally based on a title to look for.
*
* @param titleToLookFor title to look for or {@code null} for any title
* @return next section or {@code null} for end of file
*/
public Section readNextSection(String titleToLookFor) throws IOException {
String title = null;
StringBuilder keyBuilder = null;
while (true) {
String line = reader.readLine();
if (line == null) {
Preconditions.checkArgument(title == null, "missing end tag (%s)", title);
return null;
}
if (keyBuilder == null) {
Matcher m = BEGIN_PATTERN.matcher(line);
if (m.matches()) {
String curTitle = m.group(1);
if (titleToLookFor == null || curTitle.equals(titleToLookFor)) {
keyBuilder = new StringBuilder();
title = curTitle;
}
}
} else {
Matcher m = END_PATTERN.matcher(line);
if (m.matches()) {
String endTitle = m.group(1);
Preconditions.checkArgument(
endTitle.equals(title), "end tag (%s) doesn't match begin tag (%s)", endTitle, title);
return new Section(title, Base64.decodeBase64(keyBuilder.toString()));
}
keyBuilder.append(line);
}
}
}
/**
* Reads the first section in the PEM file, and then closes the reader.
*
* @param reader reader
* @return first section found or {@code null} for none found
*/
public static Section readFirstSectionAndClose(Reader reader) throws IOException {
return readFirstSectionAndClose(reader, null);
}
/**
* Reads the first section in the PEM file, optionally based on a title to look for, and then
* closes the reader.
*
* @param titleToLookFor title to look for or {@code null} for any title
* @param reader reader
* @return first section found or {@code null} for none found
*/
public static Section readFirstSectionAndClose(Reader reader, String titleToLookFor)
throws IOException {
PemReader pemReader = new PemReader(reader);
try {
return pemReader.readNextSection(titleToLookFor);
} finally {
pemReader.close();
}
}
/**
* Closes the reader.
*
* To ensure that the stream is closed properly, call {@link #close()} in a finally block.
*/
public void close() throws IOException {
reader.close();
}
/** Section in the PEM file. */
public static final class Section {
/** Title. */
private final String title;
/** Base64-decoded bytes. */
private final byte[] base64decodedBytes;
/**
* @param title title
* @param base64decodedBytes base64-decoded bytes
*/
Section(String title, byte[] base64decodedBytes) {
this.title = Preconditions.checkNotNull(title);
this.base64decodedBytes = Preconditions.checkNotNull(base64decodedBytes);
}
/** Returns the title. */
public String getTitle() {
return title;
}
/** Returns the base64-decoded bytes (modifiable array). */
public byte[] getBase64DecodedBytes() {
return base64decodedBytes;
}
}
}