public static Pkcs12Info Decode( ReadOnlyMemory <byte> encodedBytes, out int bytesConsumed, bool skipCopy = false) { AsnReader reader = new AsnReader(encodedBytes, AsnEncodingRules.BER); // Trim it to the first value encodedBytes = reader.PeekEncodedValue(); ReadOnlyMemory <byte> maybeCopy = skipCopy ? encodedBytes : encodedBytes.ToArray(); PfxAsn pfx = PfxAsn.Decode(maybeCopy, AsnEncodingRules.BER); // https://tools.ietf.org/html/rfc7292#section-4 only defines version 3. if (pfx.Version != 3) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } ReadOnlyMemory <byte> authSafeBytes = ReadOnlyMemory <byte> .Empty; Pkcs12IntegrityMode mode = Pkcs12IntegrityMode.Unknown; if (pfx.AuthSafe.ContentType == Oids.Pkcs7Data) { authSafeBytes = PkcsHelpers.DecodeOctetStringAsMemory(pfx.AuthSafe.Content); if (pfx.MacData.HasValue) { mode = Pkcs12IntegrityMode.Password; } else { mode = Pkcs12IntegrityMode.None; } } else if (pfx.AuthSafe.ContentType == Oids.Pkcs7Signed) { SignedDataAsn signedData = SignedDataAsn.Decode(pfx.AuthSafe.Content, AsnEncodingRules.BER); mode = Pkcs12IntegrityMode.PublicKey; if (signedData.EncapContentInfo.ContentType == Oids.Pkcs7Data) { authSafeBytes = signedData.EncapContentInfo.Content.GetValueOrDefault(); } if (pfx.MacData.HasValue) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } } if (mode == Pkcs12IntegrityMode.Unknown) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } List <ContentInfoAsn> authSafeData = new List <ContentInfoAsn>(); AsnReader authSafeReader = new AsnReader(authSafeBytes, AsnEncodingRules.BER); AsnReader sequenceReader = authSafeReader.ReadSequence(); authSafeReader.ThrowIfNotEmpty(); while (sequenceReader.HasData) { ContentInfoAsn.Decode(sequenceReader, out ContentInfoAsn contentInfo); authSafeData.Add(contentInfo); } ReadOnlyCollection <Pkcs12SafeContents> authSafe; if (authSafeData.Count == 0) { authSafe = new ReadOnlyCollection <Pkcs12SafeContents>(Array.Empty <Pkcs12SafeContents>()); } else { Pkcs12SafeContents[] contentsArray = new Pkcs12SafeContents[authSafeData.Count]; for (int i = 0; i < contentsArray.Length; i++) { contentsArray[i] = new Pkcs12SafeContents(authSafeData[i]); } authSafe = new ReadOnlyCollection <Pkcs12SafeContents>(contentsArray); } bytesConsumed = encodedBytes.Length; return(new Pkcs12Info { AuthenticatedSafe = authSafe, IntegrityMode = mode, _decoded = pfx, _authSafeContents = authSafeBytes, }); }
public static void EncodeAndTryEncode(Pkcs12IntegrityMode mode) { Pkcs12Builder builder1 = new Pkcs12Builder(); Pkcs12Builder builder2 = new Pkcs12Builder(); Pkcs12SafeContents contents = new Pkcs12SafeContents(); contents.AddSecret(s_zeroOid, s_derNull); builder1.AddSafeContentsUnencrypted(contents); builder2.AddSafeContentsUnencrypted(contents); int macTrailerLength = 0; if (mode == Pkcs12IntegrityMode.Password) { builder1.SealWithMac(ReadOnlySpan <char> .Empty, HashAlgorithmName.SHA1, 2); builder2.SealWithMac(ReadOnlySpan <char> .Empty, HashAlgorithmName.SHA1, 2); // Two OCTET STRINGs of 20 bytes, and the INTEGER 2 macTrailerLength = 2 + 20 + 2 + 20 + 2 + 3; } else if (mode == Pkcs12IntegrityMode.None) { builder1.SealWithoutIntegrity(); builder2.SealWithoutIntegrity(); } Assert.True(builder1.IsSealed, "builder1.IsSealed"); Assert.True(builder2.IsSealed, "builder2.IsSealed"); byte[] encoded = builder1.Encode(); byte[] buf = new byte[encoded.Length + 40]; Span <byte> bufSpan = buf; // Span too small Assert.False(builder2.TryEncode(buf.AsSpan(0, encoded.Length - 1), out int bytesWritten)); Assert.Equal(0, bytesWritten); // Span exactly right bufSpan.Fill(0xCA); Assert.True(builder2.TryEncode(buf.AsSpan(1, encoded.Length), out bytesWritten)); Assert.Equal(encoded.Length, bytesWritten); Assert.Equal(0xCA, buf[0]); Assert.Equal(0xCA, buf[bytesWritten + 1]); if (mode == Pkcs12IntegrityMode.Password) { Assert.Equal(0x02, buf[bytesWritten]); } // The same contents except the MAC (different random salt) Assert.Equal( encoded.AsSpan(0, bytesWritten - macTrailerLength).ByteArrayToHex(), buf.AsSpan(1, bytesWritten - macTrailerLength).ByteArrayToHex()); if (macTrailerLength > 0) { Assert.NotEqual( encoded.AsSpan(bytesWritten - macTrailerLength).ByteArrayToHex(), buf.AsSpan(1 + bytesWritten - macTrailerLength, macTrailerLength).ByteArrayToHex()); } // Span larger than needed bufSpan.Fill(0xCA); Assert.True(builder2.TryEncode(buf.AsSpan(2), out bytesWritten)); Assert.Equal(encoded.Length, bytesWritten); Assert.Equal(0xCA, buf[0]); Assert.Equal(0xCA, buf[1]); Assert.Equal(0xCA, buf[bytesWritten + 2]); if (mode == Pkcs12IntegrityMode.Password) { Assert.Equal(0x02, buf[bytesWritten + 1]); } // The same contents except the MAC (different random salt) Assert.Equal( encoded.AsSpan(0, bytesWritten - macTrailerLength).ByteArrayToHex(), buf.AsSpan(2, bytesWritten - macTrailerLength).ByteArrayToHex()); if (macTrailerLength > 0) { Assert.NotEqual( encoded.AsSpan(bytesWritten - macTrailerLength).ByteArrayToHex(), buf.AsSpan(2 + bytesWritten - macTrailerLength, macTrailerLength).ByteArrayToHex()); } }