private void Decrypt(ReadOnlySpan <char> password, ReadOnlySpan <byte> passwordBytes) { if (ConfidentialityMode != Pkcs12ConfidentialityMode.Password) { throw new InvalidOperationException( SR.Format( SR.Cryptography_Pkcs12_WrongModeForDecrypt, Pkcs12ConfidentialityMode.Password, ConfidentialityMode)); } EncryptedDataAsn encryptedData = EncryptedDataAsn.Decode(_encrypted, AsnEncodingRules.BER); // https://tools.ietf.org/html/rfc5652#section-8 if (encryptedData.Version != 0 && encryptedData.Version != 2) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } // Since the contents are supposed to be the BER-encoding of an instance of // SafeContents (https://tools.ietf.org/html/rfc7292#section-4.1) that implies the // content type is simply "data", and that content is present. if (encryptedData.EncryptedContentInfo.ContentType != Oids.Pkcs7Data) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } if (!encryptedData.EncryptedContentInfo.EncryptedContent.HasValue) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } List <Pkcs12SafeBag> bags; int encryptedValueLength = encryptedData.EncryptedContentInfo.EncryptedContent.Value.Length; // Don't use the array pool because the parsed bags are going to have ReadOnlyMemory projections // over this data. byte[] destination = new byte[encryptedValueLength]; int written = PasswordBasedEncryption.Decrypt( encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm, password, passwordBytes, encryptedData.EncryptedContentInfo.EncryptedContent.Value.Span, destination); try { bags = ReadBags(destination.AsMemory(0, written)); } catch { CryptographicOperations.ZeroMemory(destination.AsSpan(0, written)); throw; } _encrypted = ReadOnlyMemory <byte> .Empty; _bags = bags; ConfidentialityMode = Pkcs12ConfidentialityMode.None; }
public byte[] Encrypt(ReadOnlySpan <byte> passwordBytes, PbeParameters pbeParameters) { if (pbeParameters == null) { throw new ArgumentNullException(nameof(pbeParameters)); } PasswordBasedEncryption.ValidatePbeParameters( pbeParameters, ReadOnlySpan <char> .Empty, passwordBytes); AsnWriter pkcs8 = WritePkcs8(); AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(passwordBytes, pkcs8, pbeParameters); return(writer.Encode()); }
public void AddSafeContentsEncrypted( Pkcs12SafeContents safeContents, ReadOnlySpan <char> password, PbeParameters pbeParameters) { if (safeContents is null) { throw new ArgumentNullException(nameof(safeContents)); } if (pbeParameters is null) { throw new ArgumentNullException(nameof(pbeParameters)); } if (pbeParameters.IterationCount < 1) { throw new ArgumentOutOfRangeException(nameof(pbeParameters)); } if (safeContents.ConfidentialityMode != Pkcs12ConfidentialityMode.None) { throw new ArgumentException(SR.Cryptography_Pkcs12_CannotProcessEncryptedSafeContents, nameof(safeContents)); } if (IsSealed) { throw new InvalidOperationException(SR.Cryptography_Pkcs12_PfxIsSealed); } PasswordBasedEncryption.ValidatePbeParameters( pbeParameters, password, ReadOnlySpan <byte> .Empty); byte[] encrypted = safeContents.Encrypt(password, ReadOnlySpan <byte> .Empty, pbeParameters); if (_contents == null) { _contents = new List <ContentInfoAsn>(); } _contents.Add( new ContentInfoAsn { ContentType = Oids.Pkcs7Encrypted, Content = encrypted, }); }
public bool TryEncrypt( ReadOnlySpan <char> password, PbeParameters pbeParameters, Span <byte> destination, out int bytesWritten) { if (pbeParameters == null) { throw new ArgumentNullException(nameof(pbeParameters)); } PasswordBasedEncryption.ValidatePbeParameters( pbeParameters, password, ReadOnlySpan <byte> .Empty); AsnWriter pkcs8 = WritePkcs8(); AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(password, pkcs8, pbeParameters); return(writer.TryEncode(destination, out bytesWritten)); }
internal byte[] Encrypt( ReadOnlySpan <char> password, ReadOnlySpan <byte> passwordBytes, PbeParameters pbeParameters) { Debug.Assert(pbeParameters != null); Debug.Assert(pbeParameters.IterationCount >= 1); AsnWriter writer = null; using (AsnWriter contentsWriter = Encode()) { ReadOnlySpan <byte> contentsSpan = contentsWriter.EncodeAsSpan(); PasswordBasedEncryption.InitiateEncryption( pbeParameters, out SymmetricAlgorithm cipher, out string hmacOid, out string encryptionAlgorithmOid, out bool isPkcs12); int cipherBlockBytes = cipher.BlockSize / 8; byte[] encryptedRent = CryptoPool.Rent(contentsSpan.Length + cipherBlockBytes); Span <byte> encryptedSpan = Span <byte> .Empty; Span <byte> iv = stackalloc byte[cipherBlockBytes]; Span <byte> salt = stackalloc byte[16]; RandomNumberGenerator.Fill(salt); try { int written = PasswordBasedEncryption.Encrypt( password, passwordBytes, cipher, isPkcs12, contentsSpan, pbeParameters, salt, encryptedRent, iv); encryptedSpan = encryptedRent.AsSpan(0, written); writer = new AsnWriter(AsnEncodingRules.DER); // EncryptedData writer.PushSequence(); // version // Since we're not writing unprotected attributes, version=0 writer.WriteInteger(0); // encryptedContentInfo { writer.PushSequence(); writer.WriteObjectIdentifier(Oids.Pkcs7Data); PasswordBasedEncryption.WritePbeAlgorithmIdentifier( writer, isPkcs12, encryptionAlgorithmOid, salt, pbeParameters.IterationCount, hmacOid, iv); writer.WriteOctetString( new Asn1Tag(TagClass.ContextSpecific, 0), encryptedSpan); writer.PopSequence(); } writer.PopSequence(); return(writer.Encode()); } finally { CryptographicOperations.ZeroMemory(encryptedSpan); CryptoPool.Return(encryptedRent, clearSize: 0); writer?.Dispose(); } } }
internal bool VerifyMac( ReadOnlySpan <char> macPassword, ReadOnlySpan <byte> authSafeContents) { Debug.Assert(MacData.HasValue); HashAlgorithmName hashAlgorithm; int expectedOutputSize; string algorithmValue = MacData.Value.Mac.DigestAlgorithm.Algorithm; switch (algorithmValue) { case Oids.Md5: expectedOutputSize = 128 >> 3; hashAlgorithm = HashAlgorithmName.MD5; break; case Oids.Sha1: expectedOutputSize = 160 >> 3; hashAlgorithm = HashAlgorithmName.SHA1; break; case Oids.Sha256: expectedOutputSize = 256 >> 3; hashAlgorithm = HashAlgorithmName.SHA256; break; case Oids.Sha384: expectedOutputSize = 384 >> 3; hashAlgorithm = HashAlgorithmName.SHA384; break; case Oids.Sha512: expectedOutputSize = 512 >> 3; hashAlgorithm = HashAlgorithmName.SHA512; break; default: throw new CryptographicException( SR.Format(SR.Cryptography_UnknownHashAlgorithm, algorithmValue)); } if (MacData.Value.Mac.Digest.Length != expectedOutputSize) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } #if NETCOREAPP Debug.Assert(expectedOutputSize <= 64); // SHA512 is the largest digest size we know about Span <byte> derived = stackalloc byte[expectedOutputSize]; #else byte[] derived = new byte[expectedOutputSize]; #endif int iterationCount = PasswordBasedEncryption.NormalizeIterationCount(MacData.Value.IterationCount); Pkcs12Kdf.DeriveMacKey( macPassword, hashAlgorithm, iterationCount, MacData.Value.MacSalt.Span, derived); using (IncrementalHash hmac = IncrementalHash.CreateHMAC(hashAlgorithm, derived)) { hmac.AppendData(authSafeContents); if (!hmac.TryGetHashAndReset(derived, out int bytesWritten) || bytesWritten != expectedOutputSize) { Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} bytes when {expectedOutputSize} was expected"); throw new CryptographicException(); } return(CryptographicOperations.FixedTimeEquals( derived, MacData.Value.Mac.Digest.Span)); } }
public bool VerifyMac(ReadOnlySpan <char> password) { if (IntegrityMode != Pkcs12IntegrityMode.Password) { throw new InvalidOperationException( SR.Format( SR.Cryptography_Pkcs12_WrongModeForVerify, Pkcs12IntegrityMode.Password, IntegrityMode)); } Debug.Assert(_decoded.MacData.HasValue); HashAlgorithmName hashAlgorithm; int expectedOutputSize; string algorithmValue = _decoded.MacData.Value.Mac.DigestAlgorithm.Algorithm.Value; switch (algorithmValue) { case Oids.Md5: expectedOutputSize = 128 >> 3; hashAlgorithm = HashAlgorithmName.MD5; break; case Oids.Sha1: expectedOutputSize = 160 >> 3; hashAlgorithm = HashAlgorithmName.SHA1; break; case Oids.Sha256: expectedOutputSize = 256 >> 3; hashAlgorithm = HashAlgorithmName.SHA256; break; case Oids.Sha384: expectedOutputSize = 384 >> 3; hashAlgorithm = HashAlgorithmName.SHA384; break; case Oids.Sha512: expectedOutputSize = 512 >> 3; hashAlgorithm = HashAlgorithmName.SHA512; break; default: throw new CryptographicException( SR.Format(SR.Cryptography_UnknownHashAlgorithm, algorithmValue)); } if (_decoded.MacData.Value.Mac.Digest.Length != expectedOutputSize) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } // Cannot use the ArrayPool or stackalloc here because CreateHMAC needs a properly bounded array. byte[] derived = new byte[expectedOutputSize]; int iterationCount = PasswordBasedEncryption.NormalizeIterationCount(_decoded.MacData.Value.IterationCount); Pkcs12Kdf.DeriveMacKey( password, hashAlgorithm, iterationCount, _decoded.MacData.Value.MacSalt.Span, derived); using (IncrementalHash hmac = IncrementalHash.CreateHMAC(hashAlgorithm, derived)) { hmac.AppendData(_authSafeContents.Span); if (!hmac.TryGetHashAndReset(derived, out int bytesWritten) || bytesWritten != expectedOutputSize) { Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} bytes when {expectedOutputSize} was expected"); throw new CryptographicException(); } return(CryptographicOperations.FixedTimeEquals( derived, _decoded.MacData.Value.Mac.Digest.Span)); } }
private static ArraySegment <byte> EncodeAuthSafe( AsnWriter tmpWriter, ReadOnlyMemory <byte> encodedKeyContents, ReadOnlyMemory <byte> encodedCertContents, bool isPkcs12, string hmacOid, string encryptionAlgorithmOid, Span <byte> salt, Span <byte> certContentsIv) { Debug.Assert(tmpWriter.GetEncodedLength() == 0); tmpWriter.PushSequence(); if (!encodedKeyContents.IsEmpty) { tmpWriter.PushSequence(); tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Data); tmpWriter.PushSequence(s_contextSpecific0); ReadOnlySpan <byte> keyContents = encodedKeyContents.Span; tmpWriter.WriteOctetString(keyContents); tmpWriter.PopSequence(s_contextSpecific0); tmpWriter.PopSequence(); } if (!encodedCertContents.IsEmpty) { tmpWriter.PushSequence(); { tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Encrypted); tmpWriter.PushSequence(s_contextSpecific0); tmpWriter.PushSequence(); { // No unprotected attributes: version 0 data tmpWriter.WriteInteger(0); tmpWriter.PushSequence(); { tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Data); PasswordBasedEncryption.WritePbeAlgorithmIdentifier( tmpWriter, isPkcs12, encryptionAlgorithmOid, salt, s_windowsPbe.IterationCount, hmacOid, certContentsIv); tmpWriter.WriteOctetString(encodedCertContents.Span, s_contextSpecific0); tmpWriter.PopSequence(); } tmpWriter.PopSequence(); tmpWriter.PopSequence(s_contextSpecific0); } tmpWriter.PopSequence(); } } tmpWriter.PopSequence(); int authSafeLength = tmpWriter.GetEncodedLength(); byte[] authSafe = CryptoPool.Rent(authSafeLength); if (!tmpWriter.TryEncode(authSafe, out authSafeLength)) { Debug.Fail("TryEncode failed with a pre-allocated buffer"); throw new InvalidOperationException(); } tmpWriter.Reset(); return(new ArraySegment <byte>(authSafe, 0, authSafeLength)); }
private static ArraySegment <byte> EncodeCerts( AsnWriter tmpWriter, CertBagAsn[] certBags, AttributeAsn[] certAttrs, int certCount, Span <byte> salt, ReadOnlySpan <char> passwordSpan, Span <byte> certContentsIv, out string hmacOid, out string encryptionAlgorithmOid, out bool isPkcs12) { Debug.Assert(tmpWriter.GetEncodedLength() == 0); tmpWriter.PushSequence(); PasswordBasedEncryption.InitiateEncryption( s_windowsPbe, out SymmetricAlgorithm cipher, out hmacOid, out encryptionAlgorithmOid, out isPkcs12); using (cipher) { Debug.Assert(certContentsIv.Length * 8 == cipher.BlockSize); for (int i = certCount - 1; i >= 0; --i) { // Manually write the SafeBagAsn // https://tools.ietf.org/html/rfc7292#section-4.2 // // SafeBag ::= SEQUENCE { // bagId BAG-TYPE.&id ({PKCS12BagSet}) // bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), // bagAttributes SET OF PKCS12Attribute OPTIONAL // } tmpWriter.PushSequence(); tmpWriter.WriteObjectIdentifierForCrypto(Oids.Pkcs12CertBag); tmpWriter.PushSequence(s_contextSpecific0); certBags[i].Encode(tmpWriter); tmpWriter.PopSequence(s_contextSpecific0); if (certAttrs[i].AttrType != null) { tmpWriter.PushSetOf(); certAttrs[i].Encode(tmpWriter); tmpWriter.PopSetOf(); } tmpWriter.PopSequence(); } tmpWriter.PopSequence(); // The padding applied will add at most a block to the output, // so ask for contentsSpan.Length + the number of bytes in a cipher block. int cipherBlockBytes = cipher.BlockSize >> 3; int requestedSize = checked (tmpWriter.GetEncodedLength() + cipherBlockBytes); byte[] certContents = CryptoPool.Rent(requestedSize); int encryptedLength = PasswordBasedEncryption.Encrypt( passwordSpan, ReadOnlySpan <byte> .Empty, cipher, isPkcs12, tmpWriter, s_windowsPbe, salt, certContents, certContentsIv); Debug.Assert(encryptedLength <= requestedSize); tmpWriter.Reset(); return(new ArraySegment <byte>(certContents, 0, encryptedLength)); } }
private static ArraySegment <byte> EncodeAuthSafe( AsnWriter tmpWriter, ReadOnlyMemory <byte> encodedKeyContents, ReadOnlyMemory <byte> encodedCertContents, bool isPkcs12, string hmacOid, string encryptionAlgorithmOid, Span <byte> salt, Span <byte> certContentsIv) { Debug.Assert(tmpWriter.GetEncodedLength() == 0); tmpWriter.PushSequence(); if (!encodedKeyContents.IsEmpty) { tmpWriter.PushSequence(); tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Data); tmpWriter.PushSequence(s_contextSpecific0); ReadOnlySpan <byte> keyContents = encodedKeyContents.Span; tmpWriter.WriteOctetString(keyContents); tmpWriter.PopSequence(s_contextSpecific0); tmpWriter.PopSequence(); } if (!encodedCertContents.IsEmpty) { tmpWriter.PushSequence(); { tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Encrypted); tmpWriter.PushSequence(s_contextSpecific0); tmpWriter.PushSequence(); { // No unprotected attributes: version 0 data tmpWriter.WriteInteger(0); tmpWriter.PushSequence(); { tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Data); PasswordBasedEncryption.WritePbeAlgorithmIdentifier( tmpWriter, isPkcs12, encryptionAlgorithmOid, salt, s_windowsPbe.IterationCount, hmacOid, certContentsIv); tmpWriter.WriteOctetString(s_contextSpecific0, encodedCertContents.Span); tmpWriter.PopSequence(); } tmpWriter.PopSequence(); tmpWriter.PopSequence(s_contextSpecific0); } tmpWriter.PopSequence(); } } tmpWriter.PopSequence(); ReadOnlySpan <byte> authSafeSpan = tmpWriter.EncodeAsSpan(); byte[] authSafe = CryptoPool.Rent(authSafeSpan.Length); authSafeSpan.CopyTo(authSafe); tmpWriter.Reset(); return(new ArraySegment <byte>(authSafe, 0, authSafeSpan.Length)); }