예제 #1
0
        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;
        }
예제 #2
0
        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());
        }
예제 #3
0
        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,
            });
        }
예제 #4
0
        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));
        }
예제 #5
0
        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();
                }
            }
        }
예제 #6
0
        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));
            }
        }
예제 #7
0
        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));
            }
        }
예제 #8
0
        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));
        }
예제 #9
0
        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));
            }
        }
예제 #10
0
        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));
        }