Hi,
I'm trying to connect to npvr's web service to schedule a recording.
I translated to Delphi Prism and compiled the code from webServiceAuthentication.cs, and I get the R, RL, RTL values. I copy them to my web services client application to test, but I get "unauthorized" every time. Here is the code, can you please take a look and tell me what I'm doing wrong (hashMe the two Encrypt and button1_Click which creates R, RL, RTL). The code is just copied from webServiceAuthentication.cs and I think it should work... but it doesn't. What am I missing?
And here what my client is trying to send:
What am I doing wrong?
Thanks.
I'm trying to connect to npvr's web service to schedule a recording.
I translated to Delphi Prism and compiled the code from webServiceAuthentication.cs, and I get the R, RL, RTL values. I copy them to my web services client application to test, but I get "unauthorized" every time. Here is the code, can you please take a look and tell me what I'm doing wrong (hashMe the two Encrypt and button1_Click which creates R, RL, RTL). The code is just copied from webServiceAuthentication.cs and I think it should work... but it doesn't. What am I missing?
Code:
namespace WindowsApplication1;
interface
uses
System.Drawing,
System.Collections,
System.Collections.Generic,
System.Windows.Forms,
System.ComponentModel,
System,
System.IO,
System.Security.Cryptography,
System.Text;
type
/// <summary>
/// Summary description for MainForm.
/// </summary>
MainForm = partial class(System.Windows.Forms.Form)
private
method button1_Click(sender: System.Object; e: System.EventArgs);
method hashMe(input: string): string;
method Encrypt(clearText: string; Password: string; Salt: array of byte; iteration: Integer): string;
method Encrypt(clearData: array of byte; Key: array of byte; IV: array of byte): array of byte;
protected
method Dispose(disposing: Boolean); override;
public
constructor;
end;
implementation
{$REGION Construction and Disposition}
method MainForm.hashMe(input: string): string;
begin
var x: MD5CryptoServiceProvider := new MD5CryptoServiceProvider();
var bs: array of byte := System.Text.Encoding.UTF8.GetBytes(input);
bs := x.ComputeHash(bs);
var s: StringBuilder := new StringBuilder();
for each b: byte in bs do
begin
s.Append(b.ToString("x2").ToLower());
end;
var password: string := s.ToString();
result:=password;
end;
//Encrypt a byte array into a byte array using a key and an IV
method MainForm.Encrypt(clearData: array of byte; Key: array of byte; IV: array of byte): array of byte;
begin
// Create a MemoryStream to accept the encrypted bytes
var ms: MemoryStream := new MemoryStream();
// Create a symmetric algorithm.
// We are going to use Rijndael because it is strong and
// available on all platforms.
// You can use other algorithms, to do so substitute the
// next line with something like
// TripleDES alg = TripleDES.Create();
var alg: Rijndael := Rijndael.Create();
// Now set the key and the IV.
// We need the IV (Initialization Vector) because
// the algorithm is operating in its default
// mode called CBC (Cipher Block Chaining).
// The IV is XORed with the first block (8 byte)
// of the data before it is encrypted, and then each
// encrypted block is XORed with the
// following block of plaintext.
// This is done to make encryption more secure.
// There is also a mode called ECB which does not need an IV,
// but it is much less secure.
alg.Key := Key;
alg.IV := IV;
// Create a CryptoStream through which we are going to be
// pumping our data.
// CryptoStreamMode.Write means that we are going to be
// writing data to the stream and the output will be written
// in the MemoryStream we have provided.
var cs: CryptoStream := new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
// Write the data and make it do the encryption
cs.Write(clearData, 0, clearData.Length);
// Close the crypto stream (or do FlushFinalBlock).
// This will tell it that we have done our encryption and
// there is no more data coming in,
// and it is now a good time to apply the padding and
// finalize the encryption process.
cs.Close();
// Now get the encrypted data from the MemoryStream.
// Some people make a mistake of using GetBuffer() here,
// which is not the right way.
var encryptedData: Array of byte := ms.ToArray();
result := encryptedData;
end;
// Encrypt a string into a string using a password
// Uses Encrypt(byte[], byte[], byte[])
method MainForm.Encrypt(clearText: string; Password: string; Salt: array of byte; iteration: Integer): string;
begin
// First we need to turn the input string into a byte array.
var clearBytes: Array of byte := System.Text.Encoding.Unicode.GetBytes(clearText);
// Then, we need to turn the password into Key and IV
// We are using salt to make it harder to guess our key
// using a dictionary attack -
// trying to guess a password by enumerating all possible words.
var pdb: Rfc2898DeriveBytes := new Rfc2898DeriveBytes(Password, Salt, iteration);
// Now get the key/IV and do the encryption using the
// function that accepts byte arrays.
// Using PasswordDeriveBytes object we are first getting
// 32 bytes for the Key
// (the default Rijndael key length is 256bit = 32bytes)
// and then 16 bytes for the IV.
// IV should always be the block size, which is by default
// 16 bytes (128 bit) for Rijndael.
// If you are using DES/TripleDES/RC2 the block size is
// 8 bytes and so should be the IV size.
// You can also read KeySize/BlockSize properties off
// the algorithm to find out the sizes.
var encryptedData: Array of byte := Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));
// Now we need to turn the resulting byte array into a string.
// A common mistake would be to use an Encoding class for that.
//It does not work because not all byte values can be
// represented by characters.
// We are going to be using Base64 encoding that is designed
//exactly for what we are trying to do.
result := Convert.ToBase64String(encryptedData);
end;
constructor MainForm;
begin
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
end;
method MainForm.Dispose(disposing: Boolean);
begin
if disposing then begin
if assigned(components) then
components.Dispose();
//
// TODO: Add custom disposition code here
//
end;
inherited Dispose(disposing);
end;
{$ENDREGION}
method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
begin
var iterations: Integer := 25;
var userPassword: string := 'my_password';
var userName: string := 'my_username';
//Use the current date/time to create the Salt for the encryption of the userid and password
var timeString: string := DateTime.Now.ToUniversalTime().ToString();
var encodingSalt: Array of byte := Encoding.ASCII.GetBytes(hashMe(timeString));
//Encrypt the credentials using the entered password hash (if correct the has will match what is stored in NEWA config)
//as the encryption password along with the Salt
var pwdHash: string := hashMe(userPassword).ToLower();
var pwd: string := Encrypt(userPassword, pwdHash, encodingSalt, iterations);
var id: string := Encrypt(userName, pwdHash, encodingSalt, iterations);
var guid_: Guid := Guid.NewGuid();
//We're encrypting the date/time we used to create the salt with the guid so the decrypt of the re-passed credentials knows
//what is used as the salt
tRL.Text := Encrypt(id.Length.ToString(), pwdHash, Encoding.ASCII.GetBytes(guid_.ToString()), iterations);
var rt: string := Encrypt(timeString, pwdHash, Encoding.ASCII.GetBytes(guid_.ToString()), iterations);
tRTL.Text := Encrypt(rt.Length.ToString(), pwdHash, Encoding.ASCII.GetBytes(guid_.ToString()), iterations);
tR.Text := rt + id + pwd + guid_;
end;
end.
And here what my client is trying to send:
Code:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header><NS1:webServiceAuthentication2 xmlns:NS1="http://tempuri.org/"><NS1:R>JaqJBCCG4ofNcAphF+JfML/Jwz1Id3qoD8I08esgSr+hi3NQlrG3gXKI/TQSAU/6vnJ3LY/JqbclDXGLV1Q5JQ==SqAs+5/fRb81e1F4eAbMmmWzXUp1laHCaD5zdKsk5dU=42df2c61-b911-494d-aeab-b0dbb91dfd07</NS1:R><NS1:RL>R5MopDPnitodBgwgaLiGEA==</NS1:RL><NS1:RTL>5Vmrpqvt95tapw5D00n6QQ==</NS1:RTL></NS1:webServiceAuthentication2></SOAP-ENV:Header><SOAP-ENV:Body><scheduleRecording xmlns="http://tempuri.org/"><schedlSettings><epgeventOID>1199879</epgeventOID><quality>QUALITY_DEFAULT</quality><qualityBest>QUALITY_DEFAULT</qualityBest><qualityBetter>QUALITY_DEFAULT</qualityBetter><qualityGood>QUALITY_DEFAULT</qualityGood><qualityDefault>QUALITY_DEFAULT</qualityDefault><pre_padding_min>0</pre_padding_min><post_padding_min>0</post_padding_min><extend_end_time_min>0</extend_end_time_min><days_to_keep>0</days_to_keep><recordTimeInterval>recordOnce</recordTimeInterval><recordDayInterval>recordThisDay</recordDayInterval><dayMonday>false</dayMonday><dayTuesday>false</dayTuesday><dayWednesday>false</dayWednesday><dayThursday>false</dayThursday><dayFriday>false</dayFriday><daySaturday>false</daySaturday><daySunday>false</daySunday></schedlSettings></scheduleRecording></SOAP-ENV:Body></SOAP-ENV:Envelope>
What am I doing wrong?
Thanks.