/// <summary> /// Adds padding to the input data and returns the padded data. /// </summary> /// <param name="dataBytes">Data to be padded prior to encryption</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Padded message</returns> public byte[] EncodeMessage(byte[] dataBytes, RSAParameters @params) { //Determine if we can add padding. if (dataBytes.Length > GetMaxMessageLength(@params)) { throw new CryptographicException("Data length is too long. Increase your key size or consider encrypting less data."); } int padLength = @params.N.Length - dataBytes.Length - 3; BigInteger biRnd = new BigInteger(); biRnd.genRandomBits(padLength * 8, new Random(DateTime.Now.Millisecond)); byte[] bytRandom = null; bytRandom = biRnd.getBytes(); int z1 = bytRandom.Length; //Make sure the bytes are all > 0. for (int i = 0; i <= bytRandom.Length - 1; i++) { if (bytRandom[i] == 0x00) { bytRandom[i] = 0x01; } } byte[] result = new byte[@params.N.Length]; //Add the starting 0x00 byte result[0] = 0x00; //Add the version code 0x02 byte result[1] = 0x02; for (int i = 0; i <= bytRandom.Length - 1; i++) { z1 = i + 2; result[z1] = bytRandom[i]; } //Add the trailing 0 byte after the padding. result[bytRandom.Length + 2] = 0x00; //This starting index for the unpadded data. int idx = bytRandom.Length + 3; //Copy the unpadded data to the padded byte array. dataBytes.CopyTo(result, idx); return result; }
/// <summary> /// Import an existing set of RSA Parameters. If only the public key is to be loaded, /// Do not set the P, Q, DP, DQ, IQ or D values. If P, Q or D are set, the parameters /// will automatically be validated for existence of private key. /// </summary> /// <param name="params">RSAParameters object containing key data.</param> public void ImportParameters(RSAParameters @params) { if (Validate_Key_Data(@params)) { m_RSAParams.D = @params.D; m_RSAParams.N = @params.N; m_RSAParams.DP = @params.DP; m_RSAParams.DQ = @params.DQ; m_RSAParams.E = @params.E; m_RSAParams.IQ = @params.IQ; m_RSAParams.P = @params.P; m_RSAParams.Q = @params.Q; //Phi can only be set internally and is always calculated m_RSAParams.Phi = new BigInteger((new BigInteger(@params.P) - 1) * (new BigInteger(@params.Q) - 1)).getBytes(); m_KeyLoaded = true; } }
/// <summary> /// Sets the current class internal variables based on the supplied XML. /// Attempts to validate the XML prior to setting. /// </summary> /// <param name="xmlString">XML String containing key info</param> /// <remarks></remarks> public void FromXmlString(string xmlString) { RSAParameters oParams = new RSAParameters(); using (System.Xml.XmlReader reader = System.Xml.XmlReader.Create(new StringReader(xmlString))) { int iNode = 0; while (reader.Read()) { if (reader.Value.Trim().Length > 0) { switch (iNode) { case 1: oParams.N = Convert.FromBase64String(reader.Value); iNode = 0; break; case 2: oParams.E = Convert.FromBase64String(reader.Value); iNode = 0; break; case 3: oParams.P = Convert.FromBase64String(reader.Value); iNode = 0; break; case 4: oParams.Q = Convert.FromBase64String(reader.Value); iNode = 0; break; case 5: oParams.DP = Convert.FromBase64String(reader.Value); iNode = 0; break; case 6: oParams.DQ = Convert.FromBase64String(reader.Value); iNode = 0; break; case 7: oParams.IQ = Convert.FromBase64String(reader.Value); iNode = 0; break; case 8: oParams.D = Convert.FromBase64String(reader.Value); iNode = 0; break; } } switch (reader.NodeType) { case System.Xml.XmlNodeType.Element: switch (reader.Name.ToUpper()) { case "MODULUS": iNode = 1; break; case "EXPONENT": iNode = 2; break; case "P": iNode = 3; break; case "Q": iNode = 4; break; case "DP": iNode = 5; break; case "DQ": iNode = 6; break; case "INVERSEQ": iNode = 7; break; case "D": iNode = 8; break; } break; } } } //If P and Q are set, set Phi if (oParams.P.Length > 0 && oParams.Q.Length > 0) { oParams.Phi = new BigInteger((new BigInteger(oParams.P) - 1) * (new BigInteger(oParams.Q) - 1)).getBytes(); } if (Validate_Key_Data(oParams)) { m_RSAParams = oParams; m_KeyLoaded = true; } }
/// <summary> /// Gets the maximum message length for this padding provider. /// </summary> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Max message length</returns> public int GetMaxMessageLength(RSAParameters @params) { return @params.N.Length - 11; }
/// <summary> /// Removes padding that was added to the unencrypted data prior to encryption. /// </summary> /// <param name="dataBytes">Data to have padding removed</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Unpadded message</returns> public byte[] DecodeMessage(byte[] dataBytes, RSAParameters @params) { byte[] bytDec = new byte[@params.N.Length]; int lenDiff = 0; dataBytes.CopyTo(bytDec, lenDiff); if ((bytDec[0] != 0x0) && (bytDec[1] != 0x02)) { throw new CryptographicException("Invalid padding. Supplied data does not contain valid PKCS#1 v1.5 padding. Padding could not be removed."); } //Find out where the padding ends. int idxEnd = 0; int dataLength = 0; for (int i = 2; i < bytDec.Length; i++) { if (bytDec[i] == 0x00) { idxEnd = i; break; } } //Calculate the length of the unpadded data dataLength = bytDec.Length - idxEnd - 2; byte[] result = new byte[dataLength + 1]; int idxRslt = 0; //Put the unpadded data into the result array for (int i = idxEnd + 1; i <= bytDec.Length - 1; i++) { result[idxRslt] = bytDec[i]; idxRslt += 1; } return result; }
public static IEncryptedClient GetEncryptedClient(this IJsonServiceClient client, RSAParameters publicKey) { return(new EncryptedServiceClient(client, publicKey)); }
/// <summary> /// Create and initialize structure from RSAParameters /// </summary> /// <returns>Initialized structure</returns> /// <note>http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsaparameters.aspx</note> public static PrivateKeyBlob FromRSAParameters(RSAParameters @params) { var privateKeyBlob = new PrivateKeyBlob { Header = BlobHeader.FromRSAParameters(KeyBlobType.PrivateKeyBlob), RSAPubKey = RSAPubKey.FromRSAParameters(@params, true), }; privateKeyBlob.Modulus = new byte[@params.N.Length]; for (int i = 0; i < privateKeyBlob.Modulus.Length; i++) { privateKeyBlob.Modulus[i] = @params.N[@params.N.Length - i - 1]; } privateKeyBlob.Prime1 = new byte[@params.P.Length]; for (int i = 0; i < privateKeyBlob.Prime1.Length; i++) { privateKeyBlob.Prime1[i] = @params.P[@params.P.Length - i - 1]; } privateKeyBlob.Prime2 = new byte[@params.Q.Length]; for (int i = 0; i < privateKeyBlob.Prime2.Length; i++) { privateKeyBlob.Prime2[i] = @params.Q[@params.Q.Length - i - 1]; } privateKeyBlob.Exponent1 = new byte[@params.DP.Length]; for (int i = 0; i < privateKeyBlob.Exponent1.Length; i++) { privateKeyBlob.Exponent1[i] = @params.DP[@params.DP.Length - i - 1]; } privateKeyBlob.Exponent2 = new byte[@params.DQ.Length]; for (int i = 0; i < privateKeyBlob.Exponent2.Length; i++) { privateKeyBlob.Exponent2[i] = @params.DQ[@params.DQ.Length - i - 1]; } privateKeyBlob.Coefficient = new byte[@params.IQ.Length]; for (int i = 0; i < privateKeyBlob.Coefficient.Length; i++) { privateKeyBlob.Coefficient[i] = @params.IQ[@params.IQ.Length - i - 1]; } privateKeyBlob.PrivateExponent = new byte[@params.D.Length]; for (int i = 0; i < privateKeyBlob.PrivateExponent.Length; i++) { privateKeyBlob.PrivateExponent[i] = @params.D[@params.D.Length - i - 1]; } return privateKeyBlob; }
/// <summary> /// Create and initialize structure from RSAParameters /// </summary> /// <returns>Initialized structure</returns> public static RSAPubKey FromRSAParameters(RSAParameters @params, bool includePrivateParameters) { var rsaPubKey = new RSAPubKey { Magic = (uint)(includePrivateParameters ? 0x32415352 : 0x31415352), BitLength = (uint)(@params.N.Length << 3), }; var bytes = new byte[sizeof(uint)]; bytes[sizeof(uint) - 1] = 0; for (int i = 0; i < @params.E.Length; i++) { bytes[i] = @params.E[@params.E.Length - i - 1]; } rsaPubKey.PublicExponent = BitConverter.ToUInt32(bytes, 0); return rsaPubKey; }
/// <summary> /// Adds padding to the input data and returns the padded data. /// </summary> /// <param name="dataBytes">Data to be padded prior to encryption</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Padded message</returns> public byte[] EncodeMessage(byte[] dataBytes, RSAParameters @params) { //Iterator int i = 0; //Get the size of the data to be encrypted m_mLen = dataBytes.Length; //Get the size of the public modulus (will serve as max length for cipher text) m_k = @params.N.Length; if (m_mLen > GetMaxMessageLength(@params)) { throw new CryptographicException("Bad Data."); } //Generate the random octet seed (same length as hash) BigInteger biSeed = new BigInteger(); biSeed.genRandomBits(m_hLen * 8, new Random()); byte[] bytSeed = biSeed.getBytesRaw(); //Make sure all of the bytes are greater than 0. for (i = 0; i <= bytSeed.Length - 1; i++) { if (bytSeed[i] == 0x00) { //Replacing with the prime byte 17, no real reason...just picked at random. bytSeed[i] = 0x17; } } //Mask the seed with MFG Function(SHA1 Hash) //This is the mask to be XOR'd with the DataBlock below. byte[] dbMask = Mathematics.OAEPMGF(bytSeed, m_k - m_hLen - 1, m_hLen, m_hashProvider); //Compute the length needed for PS (zero padding) and //fill a byte array to the computed length int psLen = GetMaxMessageLength(@params) - m_mLen; //Generate the SHA1 hash of an empty L (Label). Label is not used for this //application of padding in the RSA specification. byte[] lHash = m_hashProvider.ComputeHash(System.Text.Encoding.UTF8.GetBytes(string.Empty.ToCharArray())); //Create a dataBlock which will later be masked. The //data block includes the concatenated hash(L), PS, //a 0x01 byte, and the message. int dbLen = m_hLen + psLen + 1 + m_mLen; byte[] dataBlock = new byte[dbLen]; int cPos = 0; //Current position //Add the L Hash to the data blcok for (i = 0; i <= lHash.Length - 1; i++) { dataBlock[cPos] = lHash[i]; cPos += 1; } //Add the zero padding for (i = 0; i <= psLen - 1; i++) { dataBlock[cPos] = 0x00; cPos += 1; } //Add the 0x01 byte dataBlock[cPos] = 0x01; cPos += 1; //Add the message for (i = 0; i <= dataBytes.Length - 1; i++) { dataBlock[cPos] = dataBytes[i]; cPos += 1; } //Create the masked data block. byte[] maskedDB = Mathematics.BitwiseXOR(dbMask, dataBlock); //Create the seed mask byte[] seedMask = Mathematics.OAEPMGF(maskedDB, m_hLen, m_hLen, m_hashProvider); //Create the masked seed byte[] maskedSeed = Mathematics.BitwiseXOR(bytSeed, seedMask); //Create the resulting cipher - starting with a 0 byte. byte[] result = new byte[@params.N.Length]; result[0] = 0x00; //Add the masked seed maskedSeed.CopyTo(result, 1); //Add the masked data block maskedDB.CopyTo(result, maskedSeed.Length + 1); return result; }
/// <summary> /// Removes padding that was added to the unencrypted data prior to encryption. /// </summary> /// <param name="dataBytes">Data to have padding removed</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Unpadded message</returns> public byte[] DecodeMessage(byte[] dataBytes, RSAParameters @params) { m_k = @params.D.Length; if (!(m_k == dataBytes.Length)) { throw new CryptographicException("Bad Data."); } //Length of the datablock int iDBLen = dataBytes.Length - m_hLen - 1; //Starting index for the data block. This will be equal to m_hLen + 1. The //index is zero based, and the dataBytes should start with a single leading byte, //plus the maskedSeed (equal to hash length m_hLen). int iDBidx = m_hLen + 1; //Single byte for leading byte byte bytY = 0; //Byte array matching the length of the hashing algorithm. //This array will hold the masked seed. byte[] maskedSeed = new byte[m_hLen]; //Byte array matching the length of the following: //Private Exponent D minus Hash Length, minus 1 (for the leading byte) byte[] maskedDB = new byte[iDBLen]; //Copy the leading byte bytY = dataBytes[0]; //Copy the mask Array.Copy(dataBytes, 1, maskedSeed, 0, m_hLen); //Copy the data block Array.Copy(dataBytes, iDBidx, maskedDB, 0, iDBLen); //Reproduce the seed mask from the masked data block using the mask generation function byte[] seedMask = Mathematics.OAEPMGF(maskedDB, m_hLen, m_hLen, m_hashProvider); //Reproduce the Seed from the Seed Mask. byte[] seed = Mathematics.BitwiseXOR(maskedSeed, seedMask); //Reproduce the data block bask from the seed using the mask generation function byte[] dbMask = Mathematics.OAEPMGF(seed, m_k - m_hLen - 1, m_hLen, m_hashProvider); //Reproduce the data block from the masked data block and the seed byte[] dataBlock = Mathematics.BitwiseXOR(maskedDB, dbMask); //Pull the message from the data block. First m_hLen bytes are the lHash, //followed by padding of 0x00's, followed by a single 0x01, then the message. //So we're going to start and index m_hLen and work forward. if (!(dataBlock[m_hLen] == 0x00)) { throw new CryptographicException("Decryption Error. Bad Data."); } //If we passed the 0x00 first byte test, iterate through the //data block and find the terminating character. int iDataIdx = 0; for (int i = m_hLen; i <= dataBlock.Length - 1; i++) { if (dataBlock[i] == 0x01) { iDataIdx = i + 1; break; } } //Now find the length of the data and copy it to a byte array. int iDataLen = dataBlock.Length - iDataIdx; byte[] result = new byte[iDataLen]; Array.Copy(dataBlock, iDataIdx, result, 0, iDataLen); return result; }
/// <summary> /// Adds padding to the input data and returns the padded data. /// </summary> /// <param name="dataBytes">Data to be padded prior to encryption</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Padded message</returns> byte[] IPaddingProvider.EncodeMessage(byte[] dataBytes, RSAParameters @params) { return dataBytes; }
/// <summary> /// Gets the maximum message length for this padding provider. /// </summary> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Max message length</returns> public int GetMaxMessageLength(RSAParameters @params) { return int.MaxValue; }
/// <summary> /// Verifies the signed data against the unsigned data after decryption. /// </summary> /// <param name="dataBytes">Unsigned data</param> /// <param name="signedDataBytes">Signed data (after decryption)</param> /// <param name="params">RSAParameters used for signature computation</param> /// <returns>Boolean representing whether the input data matches the signed data</returns> bool ISignatureProvider.VerifySignature(byte[] dataBytes, byte[] signedDataBytes, RSAParameters @params) { byte[] EM2 = EncodeSignature(dataBytes, @params); if (!(EM2.Length == signedDataBytes.Length)) { return false; } bool isValid = true; for (int i = 0; i <= EM2.Length - 1; i++) { if (!(EM2[i] == signedDataBytes[i])) { isValid = false; break; } } return isValid; }
/// <summary> /// Hashes and encodes the signature for encryption. Uses the DER (Distinguished Encoding Rules) /// and the SHA256 hash provider for encoding generation. /// </summary> /// <param name="dataBytes">Data to be signed</param> /// <param name="params">RSA Parameters used for signature calculation</param> /// <returns>Computed signature (pre-encryption)</returns> public byte[] EncodeSignature(byte[] dataBytes, RSAParameters @params) { //Set the intended message length (key length) int emLen = @params.N.Length; //Compute the hash of the data byte[] H = m_hashProvider.ComputeHash(dataBytes); //Get the digest encoding information for the hash being used. byte[] bytDigestEncoding = DigestEncoding.SHA256(); //Create the hashed message including the digest info byte[] T = new byte[(bytDigestEncoding.Length + m_hLen)]; bytDigestEncoding.CopyTo(T, 0); H.CopyTo(T, bytDigestEncoding.Length); H = null; bytDigestEncoding = null; if (emLen < T.Length + 11) { throw new CryptographicException("Message too short."); } //Create the padding string, octet string of 0xff byte[] PS = new byte[(emLen - T.Length - 3)]; for (int i = 0; i <= PS.Length - 1; i++) { PS[i] = 0xff; } byte[] result = new byte[emLen]; //Add the leading identifier bytes result[0] = 0x00; result[1] = 0x01; //Copy the padding string to the result PS.CopyTo(result, 2); //Add the separator byte result[PS.Length + 2] = 0x00; //Copy the digest info T.CopyTo(result, PS.Length + 3); PS = null; T = null; return result; }
/// <summary> /// Returns an RSAParameters object that contains the key data for the currently loaded key. /// </summary> /// <returns>Instance of the currently loaded RSAParameters object or null if no key is loaded</returns> /// <remarks></remarks> public RSAParameters ExportParameters(bool exportPrivate=true) { RSAParameters result = new RSAParameters(); if (m_KeyLoaded == true) { result = m_RSAParams; } else { result = null; } return result; }
/// <summary> /// Gets the maximum message length for this padding provider. /// </summary> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Max message length</returns> public int GetMaxMessageLength(RSAParameters @params) { return @params.N.Length - (2 * m_hLen) - 2; }
private bool Validate_Key_Data(RSAParameters @params) { bool result = true; //Make sure the public bits have been set if (!(@params.N.Length > 0)) { throw new CryptographicException("Value for Modulus (N) is missing or invalid."); } if (!(@params.E.Length > 0)) { throw new CryptographicException("Value for Public Exponent (E) is missing or invalid."); } //If any of the private key data (D, P or Q) were supplied, validating private //key info. if (@params.D.Length > 0 || @params.P.Length > 0 || @params.Q.Length > 0) { if (!(@params.P.Length > 0)) { throw new CryptographicException("Value for P is missing or invalid."); } if (!(@params.Q.Length > 0)) { throw new CryptographicException("Value for Q is missing or invalid."); } if (!(@params.D.Length > 0)) { throw new CryptographicException("Value for Private Exponent (D) is missing or invalid."); } //Validate the key if (@params.P.Length != @params.N.Length / 2 || @params.Q.Length != @params.N.Length / 2) { throw new CryptographicException("Invalid Key."); } BigInteger biN = new BigInteger(@params.N); BigInteger biP = new BigInteger(@params.P); BigInteger biQ = new BigInteger(@params.Q); BigInteger tmpMod = new BigInteger(biP * biQ); if (!(tmpMod == biN)) { throw new CryptographicException("Invalid Key."); } tmpMod = null; } return result; }
/// <summary> /// Adds padding to the input data and returns the padded data. /// </summary> /// <param name="dataBytes">Data to be padded prior to encryption</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Padded message</returns> public byte[] EncodeMessage(byte[] dataBytes, RSAParameters @params) { return m_OAEP.EncodeMessage(dataBytes, @params); }
/// <summary> /// Create and initialize RSAParameters structure /// </summary> /// <returns>Initialized structure</returns> /// <note>http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsaparameters.aspx</note> public RSAParameters ToRSAParameters() { var bytes = BitConverter.GetBytes(PublicExponent); var @params = new RSAParameters { E = new byte[bytes[sizeof(uint) - 1] == 0 ? sizeof(uint) - 1 : sizeof(uint)] }; for (int i = 0; i < @params.E.Length; i++) { @params.E[i] = bytes[@params.E.Length - i - 1]; } return @params; }
/// <summary> /// Gets the maximum message length for this padding provider. /// </summary> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Max message length</returns> public int GetMaxMessageLength(RSAParameters @params) { return m_OAEP.GetMaxMessageLength(@params); }
/// <summary> /// Create and initialize structure from RSAParameters /// </summary> /// <returns>Initialized structure</returns> /// <note>http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsaparameters.aspx</note> public static PublicKeyBlob FromRSAParameters(RSAParameters @params) { var publicKeyBlob = new PublicKeyBlob { Header = BlobHeader.FromRSAParameters(KeyBlobType.PublicKeyBlob), RSAPubKey = RSAPubKey.FromRSAParameters(@params, false), Modulus = new byte[@params.N.Length], }; for (int i = 0; i < publicKeyBlob.Modulus.Length; i++) { publicKeyBlob.Modulus[i] = @params.N[@params.N.Length - i - 1]; } return publicKeyBlob; }