internal static RSACryptoServiceProvider DecodePublicKey(byte[] publicKeyBytes) { MemoryStream ms = new MemoryStream(publicKeyBytes); BinaryReader rd = new BinaryReader(ms); byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; byte[] seq = new byte[15]; try { byte byteValue; ushort shortValue; shortValue = rd.ReadUInt16(); switch (shortValue) { case 0x8130: rd.ReadByte(); break; case 0x8230: rd.ReadInt16(); break; default: return(null); } seq = rd.ReadBytes(15); if (!Helpers.CompareBytearrays(seq, SeqOID)) { return(null); } shortValue = rd.ReadUInt16(); if (shortValue == 0x8103) { rd.ReadByte(); } else if (shortValue == 0x8203) { rd.ReadInt16(); } else { return(null); } byteValue = rd.ReadByte(); if (byteValue != 0x00) { return(null); } shortValue = rd.ReadUInt16(); if (shortValue == 0x8130) { rd.ReadByte(); } else if (shortValue == 0x8230) { rd.ReadInt16(); } else { return(null); } CspParameters parms = new CspParameters(); parms.Flags = CspProviderFlags.NoFlags; parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant(); parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms); RSAParameters rsAparams = new RSAParameters(); rsAparams.Modulus = rd.ReadBytes(Helpers.DecodeIntegerSize(rd)); RSAParameterTraits traits = new RSAParameterTraits(rsAparams.Modulus.Length * 8); rsAparams.Modulus = Helpers.AlignBytes(rsAparams.Modulus, traits.size_Mod); rsAparams.Exponent = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Exp); rsa.ImportParameters(rsAparams); return(rsa); } catch (Exception) { return(null); } finally { rd.Close(); } }
/// <summary> /// This helper function parses an RSA private key using the ASN.1 format /// </summary> /// <param name="privateKeyBytes">Byte array containing PEM string of private key.</param> /// <returns>An instance of <see cref="RSACryptoServiceProvider"/> rapresenting the requested private key. /// Null if method fails on retriving the key.</returns> public static RSACryptoServiceProvider DecodeRsaPrivateKey(byte[] privateKeyBytes) { MemoryStream ms = new MemoryStream(privateKeyBytes); BinaryReader rd = new BinaryReader(ms); try { byte byteValue; ushort shortValue; shortValue = rd.ReadUInt16(); switch (shortValue) { case 0x8130: // If true, data is little endian since the proper logical seq is 0x30 0x81 rd.ReadByte(); //advance 1 byte break; case 0x8230: rd.ReadInt16(); //advance 2 bytes break; default: Debug.Assert(false); // Improper ASN.1 format return(null); } shortValue = rd.ReadUInt16(); if (shortValue != 0x0102) // (version number) { Debug.Assert(false); // Improper ASN.1 format, unexpected version number return(null); } byteValue = rd.ReadByte(); if (byteValue != 0x00) { Debug.Assert(false); // Improper ASN.1 format return(null); } // The data following the version will be the ASN.1 data itself, which in our case // are a sequence of integers. // In order to solve a problem with instancing RSACryptoServiceProvider // via default constructor on .net 4.0 this is a hack CspParameters parms = new CspParameters(); parms.Flags = CspProviderFlags.NoFlags; parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant(); parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms); RSAParameters rsAparams = new RSAParameters(); rsAparams.Modulus = rd.ReadBytes(Helpers.DecodeIntegerSize(rd)); // Argh, this is a pain. From emperical testing it appears to be that RSAParameters doesn't like byte buffers that // have their leading zeros removed. The RFC doesn't address this area that I can see, so it's hard to say that this // is a bug, but it sure would be helpful if it allowed that. So, there's some extra code here that knows what the // sizes of the various components are supposed to be. Using these sizes we can ensure the buffer sizes are exactly // what the RSAParameters expect. Thanks, Microsoft. RSAParameterTraits traits = new RSAParameterTraits(rsAparams.Modulus.Length * 8); rsAparams.Modulus = Helpers.AlignBytes(rsAparams.Modulus, traits.size_Mod); rsAparams.Exponent = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Exp); rsAparams.D = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_D); rsAparams.P = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_P); rsAparams.Q = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Q); rsAparams.DP = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DP); rsAparams.DQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DQ); rsAparams.InverseQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_InvQ); rsa.ImportParameters(rsAparams); return(rsa); } catch (Exception) { Debug.Assert(false); return(null); } finally { rd.Close(); } }
// x509 public static RSACryptoServiceProvider DecodePublicKey(byte[] publicKeyBytes) { MemoryStream ms = new MemoryStream(publicKeyBytes); BinaryReader rd = new BinaryReader(ms); byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; byte[] seq = new byte[15]; try { byte byteValue; ushort shortValue; shortValue = rd.ReadUInt16(); switch (shortValue) { case 0x8130: // If true, data is little endian since the proper logical seq is 0x30 0x81 rd.ReadByte(); //advance 1 byte break; case 0x8230: rd.ReadInt16(); //advance 2 bytes break; default: Debug.Assert(false); // Improper ASN.1 format return null; } seq = rd.ReadBytes(15); //read the Sequence OID if (!Helpers.CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct return null; shortValue = rd.ReadUInt16(); if (shortValue == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) rd.ReadByte(); //advance 1 byte else if (shortValue == 0x8203) rd.ReadInt16(); //advance 2 bytes else return null; byteValue = rd.ReadByte(); if (byteValue != 0x00) { Debug.Assert(false); // Improper ASN.1 format return null; } shortValue = rd.ReadUInt16(); if (shortValue == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) rd.ReadByte(); //advance 1 byte else if (shortValue == 0x8230) rd.ReadInt16(); //advance 2 bytes else return null; // The data following the version will be the ASN.1 data itself, which in our case // are a sequence of integers. // In order to solve a problem with instancing RSACryptoServiceProvider // via default constructor on .net 4.0 this is a hack CspParameters parms = new CspParameters(); parms.Flags = CspProviderFlags.NoFlags; parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant(); parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms); RSAParameters rsAparams = new RSAParameters(); rsAparams.Modulus = rd.ReadBytes(Helpers.DecodeIntegerSize(rd)); // Argh, this is a pain. From emperical testing it appears to be that RSAParameters doesn't like byte buffers that // have their leading zeros removed. The RFC doesn't address this area that I can see, so it's hard to say that this // is a bug, but it sure would be helpful if it allowed that. So, there's some extra code here that knows what the // sizes of the various components are supposed to be. Using these sizes we can ensure the buffer sizes are exactly // what the RSAParameters expect. Thanks, Microsoft. RSAParameterTraits traits = new RSAParameterTraits(rsAparams.Modulus.Length * 8); rsAparams.Modulus = Helpers.AlignBytes(rsAparams.Modulus, traits.size_Mod); rsAparams.Exponent = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Exp); //rsAparams.D = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_D); //rsAparams.P = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_P); //rsAparams.Q = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Q); //rsAparams.DP = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DP); //rsAparams.DQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DQ); //rsAparams.InverseQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_InvQ); rsa.ImportParameters(rsAparams); return rsa; } catch (Exception e) { Debug.Assert(false); return null; } finally { rd.Close(); } }
/// <summary> /// This helper function parses an RSA private key using the ASN.1 format /// </summary> /// <param name="PrivateKeyBytes">Byte array containing PEM string of private key.</param> /// <returns>An instance of <see cref="RSACryptoServiceProvider"/> rapresenting the requested private key. /// Null if method fails on retriving the key.</returns> private static RSACryptoServiceProvider DecodeRsaPrivateKey(byte[] PrivateKeyBytes) { using (MemoryStream ms = new MemoryStream(PrivateKeyBytes)) using (BinaryReader rd = new BinaryReader(ms)) { try { byte byteValue; ushort shortValue; shortValue = rd.ReadUInt16(); switch (shortValue) { case 0x8130: // If true, data is little endian since the proper logical seq is 0x30 0x81 rd.ReadByte(); //advance 1 byte break; case 0x8230: rd.ReadInt16(); //advance 2 bytes break; default: Debug.Assert(false); // Improper ASN.1 format return null; } shortValue = rd.ReadUInt16(); if (shortValue != 0x0102) // (version number) { Debug.Assert(false); // Improper ASN.1 format, unexpected version number return null; } byteValue = rd.ReadByte(); if (byteValue != 0x00) { Debug.Assert(false); // Improper ASN.1 format return null; } // The data following the version will be the ASN.1 data itself, which in our case // are a sequence of integers. // In order to solve a problem with instancing RSACryptoServiceProvider // via default constructor on .net 4.0 this is a hack CspParameters parms = new CspParameters(); parms.Flags = CspProviderFlags.NoFlags; parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant(); parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms); RSAParameters rsAparams = new RSAParameters(); rsAparams.Modulus = rd.ReadBytes(DecodeIntegerSize(rd)); // Argh, this is a pain. From emperical testing it appears to be that RSAParameters doesn't like byte buffers that // have their leading zeros removed. The RFC doesn't address this area that I can see, so it's hard to say that this // is a bug, but it sure would be helpful if it allowed that. So, there's some extra code here that knows what the // sizes of the various components are supposed to be. Using these sizes we can ensure the buffer sizes are exactly // what the RSAParameters expect. Thanks, Microsoft. RSAParameterTraits traits = new RSAParameterTraits(rsAparams.Modulus.Length * 8); rsAparams.Modulus = AlignBytes(rsAparams.Modulus, traits.size_Mod); rsAparams.Exponent = AlignBytes(rd.ReadBytes(DecodeIntegerSize(rd)), traits.size_Exp); rsAparams.D = AlignBytes(rd.ReadBytes(DecodeIntegerSize(rd)), traits.size_D); rsAparams.P = AlignBytes(rd.ReadBytes(DecodeIntegerSize(rd)), traits.size_P); rsAparams.Q = AlignBytes(rd.ReadBytes(DecodeIntegerSize(rd)), traits.size_Q); rsAparams.DP = AlignBytes(rd.ReadBytes(DecodeIntegerSize(rd)), traits.size_DP); rsAparams.DQ = AlignBytes(rd.ReadBytes(DecodeIntegerSize(rd)), traits.size_DQ); rsAparams.InverseQ = AlignBytes(rd.ReadBytes(DecodeIntegerSize(rd)), traits.size_InvQ); rsa.ImportParameters(rsAparams); return rsa; } catch (Exception) { Debug.Assert(false); return null; } } }