public static void ImportEncryptedPem <TPass>( ReadOnlySpan <char> input, ReadOnlySpan <TPass> password, ImportEncryptedKeyAction <TPass> importAction) { bool foundEncryptedPem = false; PemFields foundFields = default; ReadOnlySpan <char> foundSlice = default; ReadOnlySpan <char> pem = input; while (PemEncoding.TryFind(pem, out PemFields fields)) { ReadOnlySpan <char> label = pem[fields.Label]; if (label.SequenceEqual(PemLabels.EncryptedPkcs8PrivateKey)) { if (foundEncryptedPem) { throw new ArgumentException(SR.Argument_PemImport_AmbiguousPem, nameof(input)); } foundEncryptedPem = true; foundFields = fields; foundSlice = pem; } Index offset = fields.Location.End; pem = pem[offset..];
/// <summary> /// Attempts to find the first PEM-encoded data. /// </summary> /// <param name="pemData"> /// The text containing the PEM-encoded data. /// </param> /// <param name="fields"> /// When this method returns, contains a value /// that specifies the location, label, and data location of the encoded data; /// or that specifies those locations as empty if no PEM-encoded data is found. /// This parameter is treated as uninitialized. /// </param> /// <returns> /// <c>true</c> if PEM-encoded data was found; otherwise <c>false</c>. /// </returns> /// <remarks> /// IETF RFC 7468 permits different decoding rules. This method /// always uses lax rules. /// </remarks> public static bool TryFind(ReadOnlySpan <char> pemData, out PemFields fields) { // Check for the minimum possible encoded length of a PEM structure // and exit early if there is no way the input could contain a well-formed // PEM. if (pemData.Length < PreEBPrefix.Length + Ending.Length * 2 + PostEBPrefix.Length) { fields = default; return(false); } const int PostebStackBufferSize = 256; Span <char> postebStackBuffer = stackalloc char[PostebStackBufferSize]; int areaOffset = 0; int preebIndex; while ((preebIndex = pemData.IndexOfByOffset(PreEBPrefix, areaOffset)) >= 0) { int labelStartIndex = preebIndex + PreEBPrefix.Length; // If there are any previous characters, the one prior to the PreEB // must be a white space character. if (preebIndex > 0 && !IsWhiteSpaceCharacter(pemData[preebIndex - 1])) { areaOffset = labelStartIndex; continue; } int preebEndIndex = pemData.IndexOfByOffset(Ending, labelStartIndex); // There is no ending sequence, -----, in the remainder of // the document. Therefore, there can never be a complete PreEB // and we can exit. if (preebEndIndex < 0) { fields = default; return(false); } Range labelRange = labelStartIndex..preebEndIndex; ReadOnlySpan <char> label = pemData[labelRange]; // There could be a preeb that is valid after this one if it has an invalid // label, so move from there. if (!IsValidLabel(label)) { goto NextAfterLabel; } int contentStartIndex = preebEndIndex + Ending.Length; int postebLength = PostEBPrefix.Length + label.Length + Ending.Length; Span <char> postebBuffer = postebLength > PostebStackBufferSize ? new char[postebLength] : postebStackBuffer; ReadOnlySpan <char> posteb = WritePostEB(label, postebBuffer); int postebStartIndex = pemData.IndexOfByOffset(posteb, contentStartIndex); if (postebStartIndex < 0) { goto NextAfterLabel; } int pemEndIndex = postebStartIndex + postebLength; // The PostEB must either end at the end of the string, or // have at least one white space character after it. if (pemEndIndex < pemData.Length - 1 && !IsWhiteSpaceCharacter(pemData[pemEndIndex])) { goto NextAfterLabel; } Range contentRange = contentStartIndex..postebStartIndex; if (!TryCountBase64(pemData[contentRange], out int base64start, out int base64end, out int decodedSize)) { goto NextAfterLabel; } Range pemRange = preebIndex..pemEndIndex; Range base64range = (contentStartIndex + base64start)..(contentStartIndex + base64end); fields = new PemFields(labelRange, base64range, pemRange, decodedSize); return(true); NextAfterLabel: if (preebEndIndex <= areaOffset) { // We somehow ended up in a situation where we will advance // backward or not at all, which means we'll probably end up here again, // advancing backward, in a loop. To avoid getting stuck, // detect this situation and return. fields = default; return(false); } areaOffset = preebEndIndex; } fields = default; return(false);
public PemFieldItem(ReadOnlySpan <char> contents, PemFields pemFields) { _contents = contents; _pemFields = pemFields; }
public void Deconstruct(out ReadOnlySpan <char> contents, out PemFields pemFields) { contents = _contents; pemFields = _pemFields; }
public Enumerator(ReadOnlySpan <char> contents) { _contents = contents; _pemFields = default; }