-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Import PEM keys #34086
Import PEM keys #34086
Conversation
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
This comment has been minimized.
This comment has been minimized.
This is not strictly related to processing PEMs since the underlying import methods already test this.
This comment has been minimized.
This comment has been minimized.
@bartonjs this is ready for review when ever you have time to spare. |
} | ||
|
||
Debug.Assert(bytesWritten == base64size); | ||
Span<byte> decodedBase64 = decodeBuffer.AsSpan(0, base64size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In bizarro-world, where TryFromBase64Chars reports a different answer in a release build, let's use the answer it gave.
Span<byte> decodedBase64 = decodeBuffer.AsSpan(0, base64size); | |
Span<byte> decodedBase64 = decodeBuffer.AsSpan(0, bytesWritten); |
foundFields = fields; | ||
foundSlice = pem; | ||
} | ||
else if (label.SequenceEqual("ENCRYPTED PRIVATE KEY")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if we need a PemHeaders constants class; but at least using a const string in this class would be better than repeating the literal across methods.
|
||
Debug.Assert(bytesWritten == base64size); | ||
Span<byte> decodedBase64 = decodeBuffer.AsSpan(0, base64size); | ||
importAction(decodedBase64, out _); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume that you've made a conscious choice to say it's OK to ignore trailing bytes here, since there's no check that the import action reports bytesRead == base64Size.
I can accept that... but a comment saying so would be nice. (Reasonable justification includes that the key has already been modified, so throwing and not have the same object modification effect)
[Fact] | ||
public void ImportFromPem_Pkcs8_Simple() | ||
{ | ||
using TAlg key = CreateKey(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use the braced using statement anywhere a using statement is used.
Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== | ||
-----END PUBLIC KEY-----"; | ||
using TAlg key = CreateKey(); | ||
AssertExtensions.Throws<ArgumentException>("input", () => key.ImportFromPem(pem)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a test that says what happens to this input when sent to the encrypted PEM import. (Either success, or still throwing). (Perhaps I'm just bad at searching/remembering)
...ies/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs
Show resolved
Hide resolved
<data name="Argument_PemImport_NoPemFound" xml:space="preserve"> | ||
<value>A supported key was not found.</value> | ||
</data> | ||
<data name="Argument_PemImport_AmbiguousPem" xml:space="preserve"> | ||
<value>Multiple supported keys were found.</value> | ||
</data> | ||
<data name="Argument_PemImport_EncryptedPem" xml:space="preserve"> | ||
<value>An encrypted key was found, but attempted to import it without a password.</value> | ||
</data> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some user friendliness enhancement suggestions:
<data name="Argument_PemImport_NoPemFound" xml:space="preserve"> | |
<value>A supported key was not found.</value> | |
</data> | |
<data name="Argument_PemImport_AmbiguousPem" xml:space="preserve"> | |
<value>Multiple supported keys were found.</value> | |
</data> | |
<data name="Argument_PemImport_EncryptedPem" xml:space="preserve"> | |
<value>An encrypted key was found, but attempted to import it without a password.</value> | |
</data> | |
<data name="Argument_PemImport_NoPemFound" xml:space="preserve"> | |
<value>No supported key formats were found. Check that the input represents the contents of a PEM-encoded key file, not the path to such a file.</value> | |
</data> | |
<data name="Argument_PemImport_AmbiguousPem" xml:space="preserve"> | |
<value>The input contains multiple keys, but only one key can be imported.</value> | |
</data> | |
<data name="Argument_PemImport_EncryptedPem" xml:space="preserve"> | |
<value>An encrypted key was found, but no password was provided. Use ImportFromEncryptedPem to import this key.</value> | |
</data> |
/// </summary> | ||
/// <param name="input">The PEM text of the key to import.</param> | ||
/// <exception cref="ArgumentException"> | ||
/// <paramref name="input"/> does not contain a PEM-encoded key with a recognized label. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe once one para is being used everything needs to be para-ified.
<exception cref="ArgumentException">
<para><paramref name="input"/> does not contain a PEM-encoded key with a recognized label.</para>
<para>-or</para>
...
</exception>
/// <remarks> | ||
/// <para> | ||
/// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels | ||
/// are found, an exception is raised to prevent importing a key when |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// are found, an exception is raised to prevent importing a key when | |
/// are found, an exception is thrown to prevent importing a key when |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems it is going to take a while longer for Ruby part of my career path to wear off.
/// When the base-64 decoded contents of <paramref name="input" /> indicate an algorithm that uses PBKDF1 | ||
/// (Password-Based Key Derivation Function 1) or PBKDF2 (Password-Based Key Derivation Function 2), | ||
/// the password is converted to bytes via the UTF-8 encoding. This method | ||
/// only supports the binary (BER/CER/DER) encoding of EncryptedPrivateKeyInfo. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this method only supports the PEM encoded version of EncryptedPrivateKeyInfo 😄.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦♂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I recall what I was trying to communicate - it just came out poorly. I was trying to say that the base-64 contents, when decoded, must be a BER, DER, CER {Encrypted}PrivateKeyInfo.
{ | ||
return ImportPkcs8PrivateKey; | ||
} | ||
else if (label.SequenceEqual("PUBLIC KEY")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these literals are spread out across multiple files, I think we should go ahead and create a PemConstants/PemHeaders/PemLabels constants file.
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs
Show resolved
Hide resolved
...tem.Security.Cryptography.Primitives/src/System/Security/Cryptography/AsymmetricAlgorithm.cs
Show resolved
Hide resolved
Use para as required. raised -> thrown. Remove confusing text around BER,CER,DER.
Ensure ArgumentException is thrown with the right reason. Improve ambiguous PEM tests.
@bartonjs I think this addresses all of your feedback. The mono/linux/X509Certificates failure is unrelated. |
Closes #31201