[External] : Re: PEM API github repo
Karl Scheibelhofer
karl.scheibelhofer.75 at gmail.com
Sat Mar 9 16:09:28 UTC 2024
... try again from from my subscribed mail account...
Hi Tony,
>
> in my jdk fork, I created a branch named pem-feedback-karl.
>
> https://github.com/KarlScheibelhofer/jdk/tree/pem-feedback-karl
>
> It is based on the pem branch of your jdk fork.
> In this pem-feedback-karl branch, I did some cleanup without changing
> the API. Your tests pass as before.
>
> My original pem-keystore implementation for the SUN provider is in this
> branch
>
> https://github.com/KarlScheibelhofer/jdk/tree/pem-keystore
>
> It did not use the PEM API.
>
> In the branch
>
> https://github.com/KarlScheibelhofer/jdk/tree/pem-keystore-pem-api
>
> I modified the PEM keystore implementation to use the PEMDecoder and
> PEMEncoder.
> To make it pass all tests, I had to fix some issues with the PEM api:
>
> * fix missing line-breaks when encoding certificates (and CRLs)
> * use uniform line length for PEM encoding keys and certificates
>
> During this work, I took some notes regarding the PEM api:
>
> * Consider decoupling the raw PEM encoding and decoding from
> SecurityObject.
> This would make the API usable for general purpose PEM encoding and
> decoding, not just for keys and certificates, as it is now.
>
> * For this PEM keystore implementation it is essential to parse the
> preceding explanatory text lines.
> The PEM decoder should support this.
> As it is now, the keystore implementation must parse the PEM objects
> itself, including reading PEM header and footer lines.
> Having to handle half the work in the application diminishes the
> value of the PEMDecoder.
> It only delegates the decoding of certificates and keys to the
> PEMDecoder.
>
> * PEMDecoder should be able to handle or pass through unknown PEM
> objects gracefully.
> Otherwise the application has to check the PEM labels in advance
> itself, which does not make sense.
>
> * Though supporting InputStream/OutputStream for reading and writing
> makes sense,
> supporting Reader/Writer feels even more essential for robust
> support for all character encodings.
> Multi-Byte character encodings won't work with InputStream/OutputStream.
>
> * The standard line separator for PEM is "\r\n".
> For PEM files stored in a typical linux file system, "\n" is
> typically used, however.
> See the output of openssl, for example.
> Because there are still several command line utilities that do not
> work well with "\r\n" line breaks.
> Supporting a different line-separator than "\r\n" is a good idea in
> my opinion.
> Base64.getMimeEncoder also supports selecting a custom line separator.
>
> * The PEMEncoder encodes the predefined SecurityObjects only.
> There is no way to use it to PEM encoded any other type of object.
> Consider opening a path to generic use.
>
> * If an application has a DER encoded certificate, it has to decode
> and parse the certificate before
> it can encode it using PEMEncoder.
> This is inconvenient.
>
> * PEMEncoder uses 64 characters as line length for private and public keys,
> and 76 characters for certificates.
> Use the same line length for all types by default.
> Consider adding support for selecting a custom line length.
> Base64.getMimeEncoder also supports selecting a line custom length.
>
> I hope this helps advancing the PEM efforts.
>
> Best regards, Karl
>
> On Thu, Jan 25, 2024 at 10:02 PM Anthony Scarpino
> <anthony.scarpino at oracle.com> wrote:
> >
> >
> > 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/security-dev/attachments/20240309/e69fbe15/attachment.htm>
More information about the security-dev
mailing list