<div dir="ltr">Hi Tony,<div><br></div><div>today, I had a look at the recent sources of the PEM API implementation - i.e. <a href="https://github.com/ascarpino/jdk/commits/pem">https://github.com/ascarpino/jdk/commits/pem</a></div><div><br></div><div>It seems that the PEM API Tests are out of sync. They fail with </div><div><br></div><div><font face="monospace">JT Harness : Tests that failed<br>java/security/PEM/PEMDecoderTest.java: Testing PEM decodings<br>java/security/PEM/PEMEncoderTest.java: Testing PEM decoding</font><br></div><div><br></div><div>I wanted to update the PEM keystore implementation to the latest code base. Can you tell about the planned next steps?</div><div><br></div><div>Regards, Karl</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, Mar 16, 2024 at 10:36 AM Karl Scheibelhofer <<a href="mailto:karl.scheibelhofer.75@gmail.com">karl.scheibelhofer.75@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Tony,<br>
<br>
find my replies inline...<br>
<br>
On Mon, Mar 11, 2024 at 6:13 AM Anthony Scarpino<br>
<<a href="mailto:anthony.scarpino@oracle.com" target="_blank">anthony.scarpino@oracle.com</a>> wrote:<br>
><br>
><br>
><br>
> On Mar 9, 2024, at 8:09 AM, Karl Scheibelhofer <<a href="mailto:karl.scheibelhofer.75@gmail.com" target="_blank">karl.scheibelhofer.75@gmail.com</a>> wrote:<br>
><br>
> <br>
> ... try again from from my subscribed mail account...<br>
><br>
>> Hi Tony,<br>
>><br>
>> in my jdk fork, I created a branch named pem-feedback-karl.<br>
>><br>
>> <a href="https://github.com/KarlScheibelhofer/jdk/tree/pem-feedback-karl" rel="noreferrer" target="_blank">https://github.com/KarlScheibelhofer/jdk/tree/pem-feedback-karl</a><br>
>><br>
>> It is based on the pem branch of your jdk fork.<br>
>> In this pem-feedback-karl branch, I did some cleanup without changing<br>
>> the API. Your tests pass as before.<br>
>><br>
>> My original pem-keystore implementation for the SUN provider is in this branch<br>
>><br>
>> <a href="https://github.com/KarlScheibelhofer/jdk/tree/pem-keystore" rel="noreferrer" target="_blank">https://github.com/KarlScheibelhofer/jdk/tree/pem-keystore</a><br>
>><br>
>> It did not use the PEM API.<br>
>><br>
>> In the branch<br>
>><br>
>> <a href="https://github.com/KarlScheibelhofer/jdk/tree/pem-keystore-pem-api" rel="noreferrer" target="_blank">https://github.com/KarlScheibelhofer/jdk/tree/pem-keystore-pem-api</a><br>
>><br>
>> I modified the PEM keystore implementation to use the PEMDecoder and PEMEncoder.<br>
>> To make it pass all tests, I had to fix some issues with the PEM api:<br>
>><br>
>> * fix missing line-breaks when encoding certificates (and CRLs)<br>
>> * use uniform line length for PEM encoding keys and certificates<br>
><br>
><br>
> It sounds like I did my repo update to use MimeEncoder after you had pulled the changes.<br>
<br>
In which repo can i see the version using MimeEncoder that you<br>
mentioned? i can't find this in<br>
<a href="https://github.com/ascarpino/jdk/commits/pem" rel="noreferrer" target="_blank">https://github.com/ascarpino/jdk/commits/pem</a><br>
<br>
><br>
>> During this work, I took some notes regarding the PEM api:<br>
>><br>
>> * Consider decoupling the raw PEM encoding and decoding from SecurityObject.<br>
>> This would make the API usable for general purpose PEM encoding and<br>
>> decoding, not just for keys and certificates, as it is now.<br>
><br>
><br>
> There has been discussions about adding a generic PEM object that would have methods to return the header, footer, and PEM text, instead of processing into a class or KeySpec. Is there something more “general purpose” you had it mind?<br>
<br>
In addition to header, footer and PEM text, for my keystore<br>
implementation, I would need the preceding PEM explanatory text lines,<br>
i.e. the lines before the header line<br>
<br>
><br>
>> * For this PEM keystore implementation it is essential to parse the<br>
>> preceding explanatory text lines.<br>
>> The PEM decoder should support this.<br>
>> As it is now, the keystore implementation must parse the PEM objects<br>
>> itself, including reading PEM header and footer lines.<br>
>> Having to handle half the work in the application diminishes the<br>
>> value of the PEMDecoder.<br>
>> It only delegates the decoding of certificates and keys to the PEMDecoder.<br>
>><br>
>> * PEMDecoder should be able to handle or pass through unknown PEM<br>
>> objects gracefully.<br>
>> Otherwise the application has to check the PEM labels in advance<br>
>> itself, which does not make sense.<br>
><br>
><br>
> So do you not have a structured data file? I expected the application would parse its own metadata, then when the structured code got to a PEM tag, it would pass the InputStream to PEMDecoder.<br>
><br>
> I am concerned about a generic PEM object storing an unknown amount of application metadata and returning it back. I feel handling non-PEM should be in the scope of the encoder/decoder.<br>
<br>
<br>
No, there is no structured data file. Just a PEM file which contains<br>
multiple PEM objects, typically certificates and private keys. It is<br>
common practice to have related certificates and keys in a single<br>
file. The explanatory text lines contain the name of the key, because<br>
this is the place specified in PEM RFC to hold meta information like<br>
this. To enable implementing a consistent Java Keystore with PEM<br>
format, I see this necessary. At least, I found no other way for<br>
handling key alias names as required by Java Keystore. If you know<br>
another way, please let me know.<br>
<br>
The files that my keystore implementation reads and writes are just<br>
completely PEM format, from first to last line.<br>
<br>
><br>
>><br>
>> * Though supporting InputStream/OutputStream for reading and writing<br>
>> makes sense,<br>
>> supporting Reader/Writer feels even more essential for robust<br>
>> support for all character encodings.<br>
>> Multi-Byte character encodings won't work with InputStream/OutputStream.<br>
><br>
><br>
> A Reader will read ahead, buffering the input data. I saw this when I had `decode(Reader)` in the API. It would return the first PEM object, but the read pointer was at the end of the file, missing the remaining PEM objects.<br>
<br>
An application or a Keystore implementation gets a stream providing<br>
multiple concatenated PEM objects that are in this keystore object. It<br>
needs to get all the PEM objects handing in that stream, i.e. in<br>
essence hand in the input stream and get out a list of pem objects<br>
containing certificates, keys and their names.<br>
<br>
><br>
> Is the multi-byte characters for the keystore metadata? I don’t see how this affects Base64.<br>
<br>
Try feeding in a UTF-16 encoded PEM file. I guess it won't work with<br>
InputStream. Because the current implementation expects one character<br>
in each byte.<br>
<br>
><br>
>><br>
>> * The standard line separator for PEM is "\r\n".<br>
>> For PEM files stored in a typical linux file system, "\n" is<br>
>> typically used, however.<br>
>> See the output of openssl, for example.<br>
>> Because there are still several command line utilities that do not<br>
>> work well with "\r\n" line breaks.<br>
>> Supporting a different line-separator than "\r\n" is a good idea in<br>
>> my opinion.<br>
>> Base64.getMimeEncoder also supports selecting a custom line separator.<br>
><br>
><br>
> The standards I saw says the line separator is “\r\n”. I realize decoders have to be more flexible because many may not follow the line separators or cut-n-paste removes them. I think having a configurable line separator for encoding is likely to create more incompatibility rather than lessen for cross-platform and using with other applications.<br>
<br>
Yes, the PEM standard say"\r\n". But widely used tools like openssl<br>
produce "\n". In practice, this is just the better solution. It goes<br>
better with most other tools and version control.<br>
I would opt for "\r\n" by default, but provide an option for "\n" only.<br>
There must be a reason why Base64.getMimeEncoder supports custom line<br>
separators.<br>
<br>
><br>
>><br>
>> * The PEMEncoder encodes the predefined SecurityObjects only.<br>
>> There is no way to use it to PEM encoded any other type of object.<br>
>> Consider opening a path to generic use.<br>
><br>
><br>
> The generic PEM object I mentioned previously I think fits this case. It would still be a SecurityObject as I don’t see value in passing in any object.<br>
<br>
I will have a look at it. Can you provide a link to your implementation?<br>
<br>
><br>
>><br>
>> * If an application has a DER encoded certificate, it has to decode<br>
>> and parse the certificate before<br>
>> it can encode it using PEMEncoder.<br>
>> This is inconvenient.<br>
><br>
><br>
> Yeah, there isn’t an EncodedKeySpec equivalent. I’d have to think if there is an easy way to do this without causing more problems. Given the purpose is going between Java Objects and PEM, accepting random data isn’t a goal. Maybe something that can be addressed with a generic PEM object.<br>
<br>
As a fallback simply allow passing in a byte[] that is base64 encoded,<br>
i.e. the result of Certificate.getEncoded().<br>
<br>
Thank you!<br>
<br>
Karl<br>
<br>
...<br>
</blockquote></div>