[External] : Re: PEM API github repo
Anthony Scarpino
anthony.scarpino at oracle.com
Thu Jan 25 21:02:16 UTC 2024
On 1/25/24 9:20 AM, Daniel Jeliński wrote:
> Hi Tony,
> Thanks for the links! The API looks very promising.
>
> Out of curiosity, why aren't you using the Base64 MIME
> encoder/decoder? They are supposed to produce/remove the newline
> characters.
I can look it over again. I had inconsistencies during testing with
expected data and returned data.
> The relationship between the byte[] and String data should be
> specified. Base64 explicitly specifies that the String APIs are
> translated to the byte[] APIs with ISO 8859-1 encoding. The PEMDecoder
> is currently using the default charset, which might produce
> interesting results if the charset is set to EBCDIC. The encoder is
> using UTF-8, which is reasonable, but given that the produced output
> will always be ASCII, ISO 8859-1 will perform better.
That's fine
> There's a disparity between the decoder and the encoder APIs; both
> work with strings, but Decoder accepts InputStream and not arrays, and
> Encoder produces byte arrays, but does not work with streams. This
> should be more uniform. I like Decoder's InputStream support (that's
> currently the only way to read multiple CA certificates from a single
> file - the String overload currently only returns the first one), so
> I'd add OutputStream support to Encoder for parity.
I don't see parity as a requirement. I see encoding and decoding as
having unique requirements. Decoding from a file or InputStream makes
sense. A decode(byte[]) method didn't seem necessary as I felt it
unlikely a user would have a single byte[] with PEM data. They were
more likely to have a String or an InputStream. The developer can wrap
it with ByteArrayInputStream or String(byte[], Charset), which is what a
public API method would do internally.
Encoding is a single operation on an object. Pass in a SecurityObject
and PEM data is returned. Returning a byte[] is the most flexible
without creating more methods. In the case where meta data is
in-between the PEM or some other data formatting.
Why should the API have an OutputStream method, something like:
pem.encodeToOutputStream(so, outputstream);
when the API as written today, the developer can use:
outputstream.writeBytes(pem.encode(obj));
I don't like to add API methods just for symmetry, they need to have a
compelling reason. I have not seen that in the OutputStream case.
> Karl's earlier
> suggestion to support Stream<SecurityObject> also makes a lot of
> sense.
I haven't counted out Stream, I just haven't seen a compelling reason.
My tests use stream() from an array list of PEM strings. But I haven't
seen the situation where the API needs stream support that couldn't be
done in a different way. This is a preview JEP, and we still have time.
If there is a compelling example, point it out to me.
> I'm not a big fan of the non-static factory methods
> withEncryption/withDecryption/withFactory. The problem with non-static
> methods that return an instance of the same type is that you need to
> check the documentation to know if the method returns a new instance
> or if it mutates the current one. Can we use static factory methods
> instead? Either that, or create a builder class.
The API states the classes are immutable which was a big requirement and
it why it's stated all over the docs. A builder class was considered
early in the API development, but it was too much complication for a few
optional cases where the developer may need to specify details like
encryption or a factory. The API has the builder idea, without the
extra builder construction methods. I don't see how a static factory
method fit here.
> I don't like the PEMEncoder.withEncryption API. It's not predictable
> enough; when encoding data, it's not consistent between writing
> unencrypted data (certificate, crl), throwing (PublicKey,
> EncryptedPrivateKeyInfo) and writing encrypted data (unencrypted
> private keys). The alternative of forcing the users to encrypt using
> EncryptedPrivateKeyInfo looks better to me.
That was a design decision to make the API easier to use. The
non-security export does not need to be burden with understanding
EncryptedPrivateKeyInfo.
The API can be consistent if you choose to only pass in
EncryptedPrivateKeyInfo and not set withEncryption(). If an
ArrayList<SecurityObject> encodes with a stream(), it is nice to
configure encryption for private keys and still encode public keys and
certificates with the same encoder.
> I'd love to see support for the OpenSSL private key formats; it seems
> that RSAPrivateCrtKeyImpl already supports PKCS#1 format, so it may be
> just a matter of exposing that functionality. Other key types like EC
> might need more work. That might be added later after the API is
> finalized.
OpenSSL 3.0 is transitioning away from their format to PKCS#8. It is an
obsoleted format. While I have thought about decoding support of RSA
PKCS#1 for compatibility, I have no intention to support OpenSSL or
PKCS#1 encoding with this PEM API.
If someone is interested in old OpenSSL or other encoding formats, that
is why the Encoder and Decoder interfaces are included. To give a
structure for developing other encoding.
>
> Thanks,
> Daniel
>
> śr., 24 sty 2024 o 22:24 Anthony Scarpino
> <anthony.scarpino at oracle.com> napisał(a):
>>
>> Hi,
>>
>> The following github link is to the PEM API as it is written in the
>> draft JEP (https://openjdk.org/jeps/8300911). There has been a few
>> changes since the original posting.
>>
>> https://urldefense.com/v3/__https://github.com/ascarpino/jdk/tree/pem__;!!ACWV5N9M2RV99hQ!NmZu22NrC2hxWJuqLHZ6l1C0KYVK0Qf_rV7tV-1uLqUb_5sFMJXyCCKVPjmEmGCeQ6US2RJquDm9AJqZXO46ju8Q$
>>
>> The Encoder and PEMEncoder to now return byte[] for the encode() method.
>> A new PEMEncoder method, encodeToString(), was added. I believe these
>> make it easier for outputting data to a file and InputStreams, while
>> still supporting a method that returns a String.
>>
>> For decode, InputStream has replaced Reader. There were comments
>> preferring InputStream and I found that Reader's buffering quirks were
>> problematic. Decoding from a byte[] is easy through an ByteArrayInputStream.
>>
>> If you are interested in testing out the API, please download and
>> compile the repo. Then let me know how your experience went.
>>
>> thanks
>>
>> Tony
More information about the security-dev
mailing list