/// <summary>
        /// Determines whether the specified RSA parameters
        /// can be represented in the CAPI format.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <returns><c>true</c> if CAPI is compatible with these parameters; <c>false</c> otherwise.</returns>
        public static bool IsCapiCompatible(RSAParameters parameters)
        {
            Requires.Argument(parameters.Modulus != null, nameof(parameters), Strings.PropertyXMustBeNonEmpty, nameof(RSAParameters.Modulus));

            // Only private keys have this restriction.
            if (!KeyFormatter.HasPrivateKey(parameters))
            {
                return(true);
            }

            int halfModulusLength = (parameters.Modulus.Length + 1) / 2;

            // These are the same assertions that Windows crypto lib itself
            // follows when it returns 'bad data'.
            // CAPI's file format does not include lengths for parameters.
            // Instead it makes some assumptions about their relative lengths
            // which make it fundamentally incompatible with some private keys
            // generated by iOS.
            return
                (halfModulusLength == parameters.P?.Length &&
                 halfModulusLength == parameters.Q?.Length &&
                 halfModulusLength == parameters.DP?.Length &&
                 halfModulusLength == parameters.DQ?.Length &&
                 halfModulusLength == parameters.InverseQ?.Length &&
                 parameters.Modulus.Length == parameters.D?.Length);
        }
Esempio n. 2
0
        /// <summary>
        /// Writes the core.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="value">The value.</param>
        protected override void WriteCore(Stream stream, RSAParameters value)
        {
            Requires.NotNull(stream, "stream");

            var sequence = new MemoryStream();

            if (KeyFormatter.HasPrivateKey(value))
            {
                // Only include the version element if this is a private key.
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, new byte[1]));
            }

            // Take care to always prepend a zero when an integer starts with a leading bit of 1,
            // since the ASN.1 spec is that integers are encoded as two's complement, and these integers
            // are always intended to be interpreted as positive.
            sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.Modulus)));
            sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.Exponent)));
            if (KeyFormatter.HasPrivateKey(value))
            {
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.D)));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.P)));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.Q)));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.DP)));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.DQ)));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, PrependLeadingZero(value.InverseQ)));
            }

            stream.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Constructed, Asn.BerTag.Sequence, sequence.ToArray()));
        }
        /// <summary>
        /// Reads a key from the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <returns>
        /// The RSA Parameters of the key.
        /// </returns>
        /// <exception cref="System.ArgumentException">
        /// Unexpected format.
        /// or
        /// Unexpected format.
        /// or
        /// Unexpected algorithm.
        /// or
        /// Unexpected format.
        /// </exception>
        protected override RSAParameters ReadCore(Stream stream)
        {
            var sequence = stream.ReadAsn1Elements().First();

            if (sequence.Class != Asn.BerClass.Universal || sequence.PC != Asn.BerPC.Constructed || sequence.Tag != Asn.BerTag.Sequence)
            {
                throw new ArgumentException("Unexpected format.");
            }

            var elements = Asn.ReadAsn1Elements(sequence.Content).ToList();

            if (elements.Count != 2 || elements[0].Class != Asn.BerClass.Universal || elements[0].PC != Asn.BerPC.Constructed || elements[0].Tag != Asn.BerTag.Sequence)
            {
                throw new ArgumentException("Unexpected format.");
            }

            var oid = Asn.ReadAsn1Elements(elements[0].Content).First();

            if (!KeyFormatter.BufferEqual(Pkcs1KeyFormatter.RsaEncryptionObjectIdentifier, oid.Content))
            {
                throw new ArgumentException("Unexpected algorithm.");
            }

            if (elements[1].Class != Asn.BerClass.Universal || elements[1].PC != Asn.BerPC.Primitive || elements[1].Tag != Asn.BerTag.BitString || elements[1].Content[0] != 0)
            {
                throw new ArgumentException("Unexpected format.");
            }

            byte[] rsaPublicKey = TrimLeadingZero(elements[1].Content);
            return(KeyFormatter.PublicKeyFilter(KeyFormatter.Pkcs1.Read(rsaPublicKey)));
        }
Esempio n. 4
0
        /// <summary>
        /// Writes the core.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="value">The value.</param>
        protected override void WriteCore(Stream stream, RSAParameters value)
        {
            Requires.NotNull(stream, "stream");

            var sequence = new MemoryStream();

            if (KeyFormatter.HasPrivateKey(value))
            {
                // Only include the version element if this is a private key.
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, new byte[1]));
            }

            sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, this.prependLeadingZeroOnCertainElements ? PrependLeadingZero(value.Modulus) : value.Modulus));
            sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, value.Exponent));
            if (KeyFormatter.HasPrivateKey(value))
            {
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, value.D));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, this.prependLeadingZeroOnCertainElements ? PrependLeadingZero(value.P) : value.P));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, this.prependLeadingZeroOnCertainElements ? PrependLeadingZero(value.Q) : value.Q));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, this.prependLeadingZeroOnCertainElements ? PrependLeadingZero(value.DP) : value.DP));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, value.DQ));
                sequence.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Primitive, Asn.BerTag.Integer, this.prependLeadingZeroOnCertainElements ? PrependLeadingZero(value.InverseQ) : value.InverseQ));
            }

            stream.WriteAsn1Element(new Asn.DataElement(Asn.BerClass.Universal, Asn.BerPC.Constructed, Asn.BerTag.Sequence, sequence.ToArray()));
        }
        /// <summary>
        /// Reads a key from the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <returns>
        /// The RSA Parameters of the key.
        /// </returns>
        protected override RSAParameters ReadCore(Stream stream)
        {
            var parameters = default(RSAParameters);

            using var reader = new BinaryReader(stream);

            bool hasPrivateKey;
            byte keyBlobHeader = reader.ReadByte();

            switch (keyBlobHeader)
            {
            case PrivateKeyBlobHeader:
                hasPrivateKey = true;
                break;

            case PublicKeyBlobHeader:
                hasPrivateKey = false;
                break;

            default:
                throw KeyFormatter.FailFormat();
            }

            byte currentBlobVersion = reader.ReadByte();

            KeyFormatter.VerifyFormat(currentBlobVersion == CurrentBlobVersion);

            short reserved = reader.ReadInt16();

            KeyFormatter.VerifyFormat(reserved == 0);

            int keySpec = reader.ReadInt32();

            KeyFormatter.VerifyFormat(keySpec == KeySpecKeyExchange);

            string magicHeader = Encoding.UTF8.GetString(reader.ReadBytes(4), 0, 4);

            KeyFormatter.VerifyFormat(hasPrivateKey ? (magicHeader == PrivateKeyMagicHeader) : (magicHeader == PublicKeyMagicHeader));

            int bitlen  = reader.ReadInt32();
            int bytelen = bitlen / 8;

            parameters.Exponent = ReadReversed(reader, 4);
            parameters.Modulus  = ReadReversed(reader, bytelen);

            if (hasPrivateKey)
            {
                parameters.P        = ReadReversed(reader, bytelen / 2);
                parameters.Q        = ReadReversed(reader, bytelen / 2);
                parameters.DP       = ReadReversed(reader, bytelen / 2);
                parameters.DQ       = ReadReversed(reader, bytelen / 2);
                parameters.InverseQ = ReadReversed(reader, bytelen / 2);
                parameters.D        = ReadReversed(reader, bytelen);
            }

            return(parameters);
        }
        /// <summary>
        /// Reads a key from the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <returns>
        /// The RSA Parameters of the key.
        /// </returns>
        protected override RSAParameters ReadCore(Stream stream)
        {
            var universalConstructedSequence = stream.ReadAsn1Elements().Single();
            var sequence = Asn.ReadAsn1Elements(universalConstructedSequence.Content).ToList();

            KeyFormatter.VerifyFormat(sequence[0].Content.Length == 1 && sequence[0].Content[0] == 0x00, Strings.UnrecognizedVersion);
            Asn.DataElement oid = Asn.ReadAsn1Elements(sequence[1].Content).First();
            KeyFormatter.VerifyFormat(X509SubjectPublicKeyInfoFormatter.BufferEqual(oid.Content, Pkcs1KeyFormatter.RsaEncryptionObjectIdentifier), Strings.UnrecognizedObjectIdentifier);
            return(KeyFormatter.Pkcs1.Read(sequence[2].Content));
        }
 /// <summary>
 /// Throws an exception if the specified RSAParameters cannot be
 /// serialized in the CAPI format.
 /// </summary>
 /// <param name="parameters">The RSA parameters.</param>
 internal static void VerifyCapiCompatibleParameters(RSAParameters parameters)
 {
     try
     {
         KeyFormatter.VerifyFormat(IsCapiCompatible(parameters), "Private key parameters have lengths that are not supported by CAPI.");
     }
     catch (FormatException ex)
     {
         throw new NotSupportedException(ex.Message, ex);
     }
 }
Esempio n. 8
0
        /// <summary>Writes a key to the specified stream.</summary>
        /// <param name="stream">The stream.</param>
        /// <param name="parameters">The RSA parameters of the key.</param>
        protected override void WriteCore(Stream stream, RSAParameters parameters)
        {
            if (!IsCapiCompatible(parameters))
            {
                // Try to get the RSA parameters to conform to CAPI's requirements.
                parameters = NegotiateSizes(parameters);
            }

            VerifyCapiCompatibleParameters(parameters);

            var writer = new BinaryWriter(stream);

            int bytelen = parameters.Modulus[0] == 0 // if high-order byte is zero, it's for sign bit; don't count in bit-size calculation
                ? parameters.Modulus.Length - 1
                : parameters.Modulus.Length;
            int bitlen = 8 * bytelen;

            writer.Write(KeyFormatter.HasPrivateKey(parameters) ? PrivateKeyBlobHeader : PublicKeyBlobHeader);
            writer.Write(CurrentBlobVersion);
            writer.Write((short)0); // reserved
            writer.Write(KeySpecKeyExchange);
            writer.Write(Encoding.UTF8.GetBytes(KeyFormatter.HasPrivateKey(parameters) ? PrivateKeyMagicHeader : PublicKeyMagicHeader));
            writer.Write(bitlen);

            // Ensure that the exponent occupies 4 bytes in the serialized stream,
            // even if in the parameters structure it does not.
            // We cannot use BitConverter.ToInt32 to help us do this because
            // its behavior varies based on the endianness of the platform,
            // yet RSAParameters is defined to always be Big Endian, and the
            // key blob format is defined to always be Little Endian, so we have to be careful.
            byte[] exponentPadding = new byte[4 - parameters.Exponent.Length];
            WriteReversed(writer, parameters.Exponent);
            writer.Write(exponentPadding);

            // bytelen drops the sign byte if it is present (which is good)
            WriteReversed(writer, parameters.Modulus, bytelen);

            if (KeyFormatter.HasPrivateKey(parameters))
            {
                WriteReversed(writer, parameters.P, bytelen / 2);
                WriteReversed(writer, parameters.Q, bytelen / 2);
                WriteReversed(writer, parameters.DP, bytelen / 2);
                WriteReversed(writer, parameters.DQ, bytelen / 2);
                WriteReversed(writer, parameters.InverseQ, bytelen / 2);
                WriteReversed(writer, parameters.D, bytelen);
            }

            writer.Flush();
            writer.Dispose();
        }
Esempio n. 9
0
        /// <summary>
        /// Reads a key from the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <returns>
        /// The RSA Parameters of the key.
        /// </returns>
        protected override RSAParameters ReadCore(Stream stream)
        {
            var keyBlobElement = Asn.ReadAsn1Elements(stream).First();

            KeyFormatter.VerifyFormat(
                keyBlobElement.Class == Asn.BerClass.Universal &&
                keyBlobElement.PC == Asn.BerPC.Constructed &&
                keyBlobElement.Tag == Asn.BerTag.Sequence);

            stream = new MemoryStream(keyBlobElement.Content);
            var sequence = Asn.ReadAsn1Elements(stream).ToList();

            switch (sequence.Count)
            {
            case 2:
                return(new RSAParameters
                {
                    Modulus = sequence[0].Content,
                    Exponent = sequence[1].Content,
                });

            case 9:
                KeyFormatter.VerifyFormat(sequence[0].Content.Length == 1 && sequence[0].Content[0] == 0, "Unsupported version.");
                return(new RSAParameters
                {
                    Modulus = sequence[1].Content,
                    Exponent = sequence[2].Content,
                    D = sequence[3].Content,
                    P = sequence[4].Content,
                    Q = sequence[5].Content,
                    DP = sequence[6].Content,
                    DQ = sequence[7].Content,
                    InverseQ = sequence[8].Content,
                });

            default:
                throw KeyFormatter.FailFormat();
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Determines whether the specified RSA parameters
        /// can be represented in the CAPI format.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <returns><c>true</c> if CAPI is compatible with these parameters; <c>false</c> otherwise.</returns>
        internal static bool IsCapiCompatible(RSAParameters parameters)
        {
            // Only private keys have this restriction.
            if (!KeyFormatter.HasPrivateKey(parameters))
            {
                return(true);
            }

            int halfModulusLength = (parameters.Modulus.Length + 1) / 2;

            // These are the same assertions that Windows crypto lib itself
            // follows when it returns 'bad data'.
            // CAPI's file format does not include lengths for parameters.
            // Instead it makes some assumptions about their relative lengths
            // which make it fundamentally incompatible with some private keys
            // generated by iOS.
            return
                (halfModulusLength == parameters.P.Length &&
                 halfModulusLength == parameters.Q.Length &&
                 halfModulusLength == parameters.DP.Length &&
                 halfModulusLength == parameters.DQ.Length &&
                 halfModulusLength == parameters.InverseQ.Length &&
                 parameters.Modulus.Length == parameters.D.Length);
        }