I found an article describing how to connect existing membership with OpenID but when user uses some OpenID provider to login first time my app creates account from him, it puts his authenticate link as username, and display name as comment.How am I currently determining what to show as username:
string username = Membership.GetUser(UserID).UserName;
return string.IsNullOrEmpty(Membership.GetUser(UserID).Comment) ? username : Membership.GetUser(username).Comment;
This really isn't a problem,but now I have to link somehow to user profile page, and I am not sure how to do that, here is an example of what could work for me:
www.example.com/users/Guid/DisplayName
Display name is either username if he registered through my page or comment if user used OpenID provider do create account.
if I did something like:
www.example.com/users/DisplayName
I'm not sure it won't display wrong user since someone could regeister username "Foo" through membership and some other user is using that username with OpenID so he would get "Foo" in his comment field
So to finish my question, would it be bad to put user GUID in routed url as I saw similar stuff on many other websites,or is there way to derive integer from GUID back and forth?
A GUID can certainly be put into a URL (probably without curly braces around it). Alternatively as a 128-bit number, it can also be represented in a base64 string, which would be shorter than a GUID. Either one is pretty user-unfriendly, but your concern about collisions between different types of user accounts seems justified.
Here is how you could convert a GUID into a base64 web-safe string. Code snippets courtesy of DotNetOpenAuth utilities).
Guid userGuid; // value comes from your database
ConvertToBase64WebSafeString(userGuid.ToByteArray());
/// <summary>
/// Converts to data buffer to a base64-encoded string, using web safe characters and with the padding removed.
/// </summary>
/// <param name="data">The data buffer.</param>
/// <returns>A web-safe base64-encoded string without padding.</returns>
internal static string ConvertToBase64WebSafeString(byte[] data) {
var builder = new StringBuilder(Convert.ToBase64String(data));
// Swap out the URL-unsafe characters, and trim the padding characters.
builder.Replace('+', '-').Replace('/', '_');
while (builder[builder.Length - 1] == '=') { // should happen at most twice.
builder.Length -= 1;
}
return builder.ToString();
}
And of course convert back from the URL base64 string to a Guid:
string base64SegmentFromUrl; // from incoming web request to profile page
Guid userGuid = new Guid(FromBase64WebSafeString(base64SegmentFromUrl);
/// <summary>
/// Decodes a (web-safe) base64-string back to its binary buffer form.
/// </summary>
/// <param name="base64WebSafe">The base64-encoded string. May be web-safe encoded.</param>
/// <returns>A data buffer.</returns>
internal static byte[] FromBase64WebSafeString(string base64WebSafe) {
Requires.NotNullOrEmpty(base64WebSafe, "base64WebSafe");
Contract.Ensures(Contract.Result<byte[]>() != null);
// Restore the padding characters and original URL-unsafe characters.
int missingPaddingCharacters;
switch (base64WebSafe.Length % 4) {
case 3:
missingPaddingCharacters = 1;
break;
case 2:
missingPaddingCharacters = 2;
break;
case 0:
missingPaddingCharacters = 0;
break;
default:
throw ErrorUtilities.ThrowInternal("No more than two padding characters should be present for base64.");
}
var builder = new StringBuilder(base64WebSafe, base64WebSafe.Length + missingPaddingCharacters);
builder.Replace('-', '+').Replace('_', '/');
builder.Append('=', missingPaddingCharacters);
return Convert.FromBase64String(builder.ToString());
}
Related
I'm currently working on a public library that will allow you to create a dhcp package with all kinds of options like dhcp options. When you create it, a byte array is created, which you can send as payload via a UdpSocket. I have created two classes for this: 1. dhcppacket, 2. dhcpoption. So far the creation of a Dhcp Packet with any DhcpOptions works already.
Now I want to parse incoming DhcpPackets, which are passed in form of a byte array, back to a DhcpPacket object to be able to read important information like the message type or from the passed DhcpOptions like subnet mask. The fixed part (beginning) of the DhcpPacket I have already agreed. But now I fail with the DhcpOptions, because they are variable. How to create X objects from a byte array.
Given is a byte-array with not given length
dhcpOptions = pPayload.Skip(240).ToArray();
From this payload I now want to create objects of the class "DhcpOption". Each Dhcp Option starts with a unique byte e.g. 0x01 = DhcpOption containing the subnet mask. Then follows a byte, which indicates the length of the DhcpOption including the following values. If, for example, a DNS server is communicated via a Dhcp option, the length is 0x04, since an IPv4 address is transferred as value. The total length of the Dhcp option in the payload is therefore 6 bytes. But since all options do not have the same length, I do not know how exactly I get to the start of the next DHCP option.
Example for creating a dhcp option from the ByteArray:
DhcpOption option = new DhcpOption{
optionId = byteArray[0],
optionLength = byteArray[1],
optionValue = byteArray.Skip(2).Take(optionLength).ToArray(),
};
Searched are X-Objects of the class:
public class DhcpOption
{
/// <summary>
/// Define the DHCP options to be created by name
/// </summary>
public dhcpOptionIds optionId { get; set; } = new dhcpOptionIds();
private byte[] optionIdBytes = new byte[] { };
/// <summary>
/// Define the required length for the optionValue
/// </summary>
public byte[] optionLength { get; set; } = new byte[] { };
/// <summary>
/// Define the value for the option e.g. subnet mask
/// </summary>
public byte[] optionValue { get; set; } = new byte[] { };
/// <summary>
/// Create the DHCP option as byte array. Is then specified as an option in the DhcpPacket.
/// </summary>
/// <returns></returns>
public byte[] buildDhcpOption()
{
object selected = Convert.ChangeType(optionId, optionId.GetTypeCode());
optionIdBytes = new byte[] { Convert.ToByte(selected, null) };
return optionIdBytes.Concat(optionLength).Concat(optionValue).ToArray();
}
}
Instead of a pure byte array I would like a list of dhcp options. So List<DhcpOption> can be used to search for a desired Dhcp-option with for or foreach and then read out the value.
Here is the GitHub repo, so I don't have to copy the code.
https://github.com/Marschall-dev/DhcpDotNet/blob/main/DhcpDotNet/DhcpDotNet/DhcpPacket.cs
If you have a fixed protocol you need to follow, the typical approach I use is a BinaryReader/BinaryWriter.
Create a memory stream from the byte-array and feed to the binaryReader. If you have a list of multiple different options, the data for each option should be prefixed by the type of option, and optionally, length. So you can first read the type of option, and then have a switch that handles each specific option type. Repeat this until all options are read.
Serializing and deserializing should usually be the inverse of each other, so you might want to use BinaryWriter for serialization. Unit tests are very useful to ensure you can serialize and deserialize messages without involving any network connectivity. If you do not need to follow a existing protocol I would recommend a serialization library, like protobuf.net.
I am using ASP.NET MVC 4, the .NET Braintree Payments API, and Braintree.js.
Here is a simple wrapper I have built for Braintree:
public class PaymentBL
{
private static BraintreeGateway _braintreeGateway = new BraintreeGateway
{
Environment = Braintree.Environment.SANDBOX,
MerchantId = "xxxxxxx",
PublicKey = "xxxxxxxxxxxx",
PrivateKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
};
public Result<Transaction> ChargeCardOnce(decimal amount, string cardholderName, string cardNumber, string expiration,
string cvv)
{
TransactionCreditCardRequest creditCardRequest = new TransactionCreditCardRequest();
creditCardRequest.CardholderName = cardholderName;
creditCardRequest.Number = cardNumber;
creditCardRequest.ExpirationDate = expiration;
creditCardRequest.CVV = cvv;
TransactionOptionsRequest optionsRequest = new TransactionOptionsRequest();
optionsRequest.SubmitForSettlement = true;
TransactionRequest transactionRequest = new TransactionRequest();
transactionRequest.Amount = amount;
transactionRequest.CreditCard = creditCardRequest;
transactionRequest.Options = optionsRequest;
return _braintreeGateway.Transaction.Sale(transactionRequest);
}
/// <summary>
/// Stores a credit card in the Braintree vault. In some cases, will put a $1 temporary charge
/// on the credit card that will come off a few days later.
///
/// From BrainTree: Regardless of card type, any instance where a $1 authorization returns a successful result,
/// we immediately follow-up with an automatic void request to ensure that the transaction will fall off
/// of the cardholder's statement as soon as possible.
/// </summary>
public Result<CreditCard> StoreCustomer(int customerId, string cardholderName, string cardNumber, string expiration, string cvv)
{
//CreditCardAddressRequest addressRequest = new CreditCardAddressRequest();
//addressRequest.PostalCode = postalCode;
CreditCardOptionsRequest optionsRequest = new CreditCardOptionsRequest();
optionsRequest.VerifyCard = true;
optionsRequest.VerificationMerchantAccountId = _braintreeGateway.MerchantId;
CreditCardRequest creditCard = new CreditCardRequest();
creditCard.CustomerId = customerId.ToString();
creditCard.Token = customerId.ToString(); // Use same token to ensure overwrite
creditCard.CardholderName = cardholderName;
creditCard.Number = cardNumber;
creditCard.ExpirationDate = expiration;
creditCard.CVV = cvv;
creditCard.Options = optionsRequest;
return _braintreeGateway.CreditCard.Create(creditCard);
}
/// <summary>
/// Search BrainTree vault to retrieve credit card
/// </summary>
/// <param name="customerId"></param>
public CreditCard GetCreditCardOnFile(int customerId)
{
Customer customer = null;
try
{
customer = _braintreeGateway.Customer.Find(customerId.ToString());
}
catch (Braintree.Exceptions.NotFoundException)
{
return null;
}
if (customer.CreditCards == null || customer.CreditCards.Length == 0)
{
return null;
}
if (customer.CreditCards.Length >= 2)
{
throw new Exception(string.Format("Customer {0} has {1} credit cards",
customerId, customer.CreditCards.Length));
}
return customer.CreditCards[0];
}
}
When I call this method, it works:
Result<Transaction> result = paymentBL.ChargeCardOnce(
2.34m
, formCollection["name"]
, formCollection["number"]
, formCollection["exp"]
, formCollection["cvv"]
);
Subsequently, I can view the completed test transactions in the Braintree dashboard. Therefore, I know that the encrypted form values from Braintree.js are arriving at my controller action correctly, and that my keys and merchant account IDs are all set up correctly.
When I replace the above call to ChargeCardOnce with the below call to StoreCustomer, I receive an Braintree.Exceptions.AuthorizationException at the line return _braintreeGateway.CreditCard.Create(creditCard);
Result<CreditCard> result = paymentBL.StoreCustomer(
systemHost.Customer.CustomerId
, formCollection["name"]
, formCollection["number"]
, formCollection["exp"]
, formCollection["cvv"]
);
From Braintree support: "You are able to create a customer as well as a credit card in the sandbox, as it is built to exactly mirror what the production environment would look like."
Has anyone experience this also? I'm referring Braintree support to this question, but if anyone on SO has seen this and knows a solution or workaround, I would be much relieved.
I work at Braintree. It looks like we already got back to you with the answer to your question, but I'll answer here as well for anyone who has the same issue in the future.
In this case, the problem is:
optionsRequest.VerificationMerchantAccountId = _braintreeGateway.MerchantId;
Your merchant ID identifies your payment gateway account, while your merchant account ID identifies the bank account you want to use for the verification. An article in our support center explains the difference:
MerchantAccountId
With Braintree, you can have multiple merchant
accounts all processing via the same gateway account. This
means that you can have multiple locations, multiple businesses,
multiple currencies, etc. all setup and processing under a single
account. This makes it easy to keep track of all of your processing
via unified reporting and access and can even save you money.
You can find the IDs for all merchant accounts in your gateway
account by following these steps:
login to your account
hover over your account name
and click "Processing"
scroll to the bottom of the page to the
section labeled "Merchant Accounts".
An AuthorizationException is HTTP Status Code 403 Forbidden. It means that the server is declining your request, because you don't have permission to access a resource (even though you may be authenticated).
Since there is no merchant account available to your user with the ID you specify (since it's not a merchant account ID at all), you get the AuthorizationException.
If, as is often the case, your merchant has only one merchant account, or you want to use the default account, it's not necessary to specify a VerificationMerchantAccountId.
I'm trying to establish access to an embedded SQL resource file I've created in a Class Library. However, I'm not sure where to go from here.
I've accessed the resource using:
Assembly.GetExcecutingAssembly().GetManifestResourceStream("InsertTest.sql");
My understanding is that there is a way to access them in a strongly typed fashion, but I can't seem to get a handle on the project or the solution to browse through their respective properties or resources programatically.
What am I missing?
Although I did get some great suggestions (see Philip Daniels' answer - good stuff), none of them really addressed my specific concerns. However, I found that the easiest way to accomplish this was to do the following:
Right click your project and select 'Properties'
Select the 'Resources' tab. Create a new resources file if necessary.
In the upper left hand corner there is a drop down that defaults to 'Strings'. Click this box and choose 'Files'.
Drag and drop the resource file you'd like to embed in the project.
You can now access a strongly typed resource using the following syntax:
Project.Properties.Resources.ResourceName;
In my situation, this worked perfectly as I am storing inline SQL in these files and it returns the sql embedded in the file. Keep in mind, however, that by defaults these resources are linked and not embedded, but you can change their property to set them to embedded.
Hope this helps someone!
You're almost there. I have a couple of functions I use for this. You can do somehting very similar for images. I'm not sure it's worth creating properties like you want (you can do that through the Resources tab of the project properties if you insist).
/// <summary>
/// Gets an open stream on the specified embedded resource. It is the
/// caller's responsibility to call Dispose() on the stream.
/// The filename is of the format "folder.folder.filename.ext"
/// and is case sensitive.
/// </summary>
/// <param name="assembly">The assembly from which to retrieve the Stream.</param>
/// <param name="filename">Filename whose contents you want.</param>
/// <returns>Stream object.</returns>
public static Stream GetStream(Assembly assembly, string filename)
{
string name = String.Concat(assembly.GetName().Name, ".", filename);
Stream s = assembly.GetManifestResourceStream(name);
return s;
}
/// <summary>
/// Get the contents of an embedded file as a string.
/// The filename is of the format "folder.folder.filename.ext"
/// and is case sensitive.
/// </summary>
/// <param name="assembly">The assembly from which to retrieve the file.</param>
/// <param name="filename">Filename whose contents you want.</param>
/// <returns>String object.</returns>
public static string GetFileAsString(Assembly assembly, string filename)
{
using (Stream s = GetStream(assembly, filename))
using (StreamReader sr = new StreamReader(s))
{
string fileContents = sr.ReadToEnd();
return fileContents;
}
}
On a resource file you won't be able to have intellisense to build your sql script compare to have them as separate files in your project. You can create a helper class to access them in a strong type fashion:
public class Scripts
{
public static string Sql1
{
get
{
return GetResource("sql1.sql");
}
}
public static string Sql2
{
get
{
return GetResource("sql2.sql");
}
}
private static string GetResource(string name)
{
var assembly = Assembly.GetExecutingAssembly();
using(var stream = new StreamReader(assembly.GetManifestResourceStream("Myproject.Sql." + name)))
{
return stream.ReadToEnd();
}
}
}
For example, in Dapper, you can access your scripts like this:
using(var db = new SqlConnection("yourconnectionstring")){
db.Open();
var results = db.Query(Scripts.Sql1);
}
I have a database that is currently using AES 128. The database has about 8 million records, and what the client wants is to decode the passwords and hash them instead so the passwords cannot be decrypted. This is a web app with data stored on a remote server. I tried using a web app to do the conversion, but it keeps timing out. Since this is 8 mil, it will take a while to go through all the items, so my next idea was to get SQL do do the decryption and hashing. I could let it run for the next few days.
The problem I am having is that each column has the encrypted password with a unique salt. I can't find a function to decrypt the password using the encrypted password and salt. Is there a function? Even third party? Is there a better way to go about this?
Thanks!
The easiest/only way to do this in SQL Server would by to write a CLR User-Defined Function (UDF) in C#. See
SQL Server 2005: Creating Your First C# CLR UDF in 10 Easy Steps (One of Which Includes Partying)
SQLCLR - Create CLR User-Defined Function ( UDF ) - Check Constraint on EmailAddress Column Using RegEx
MSDN: CLR User-Defined Functions (ADO.NET)
for more details. If it was me, I'd add a new column to contain the new password hash and run an update statement periodically to construct the new password hash, something like this:
update top 10000 dbo.users
set hashedPassword = DecryptAndHash( encryptedPassword )
where hashedPassword is null
where DecryptAndHash() is your CLR UDF. Once the transform is complete, you should be free to drop the old column and roll out the update to use the new authentication logic.
Probably want to put an trigger on the table to keep the hash in sync with the encrypted password in case anybody changes their password while all this is going on.
FWIW, the code shouldn't be much more complicated than
using System;
using Microsoft.SqlServer.Server;
namespace Sandbox
{
public static class EncryptionFunctions
{
/// <summary>
/// Encrypts a string
/// </summary>
/// <param name="plainText"></param>
/// <returns>varbinary</returns>
[SqlFunction]
public static byte[] Encrypt( string plainText )
{
byte[] cipherText ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
cipherText = cipher.Encrypt( plainText ) ;
}
return cipherText ;
}
/// <summary>
/// Decrypts a previously encrypted varbinary
/// </summary>
/// <param name="cipherText"></param>
/// <returns>string</returns>
[SqlFunction]
public static string Decrypt( byte[] cipherText )
{
string plainText ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
plainText = cipher.Decrypt( cipherText ) ;
}
return plainText ;
}
/// <summary>
/// Compute the secure hash of a [plaintext] string
/// </summary>
/// <param name="plainText"></param>
/// <returns> varbinary </returns>
[SqlFunction]
public static byte[] SecureHash( string plainText )
{
byte[] hash ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
hash = cipher.ComputeSecureHash( plainText ) ;
}
return hash ;
}
/// <summary>
/// Convenience wrapper method to take a previously encrypted string, decrypt it and compute its secure hash
/// </summary>
/// <param name="cipherText"></param>
/// <returns>varbinary</returns>
[SqlFunction]
public static byte[] DecryptAndHash( byte[] cipherText )
{
byte[] hash ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
hash = cipher.ComputeSecureHash( cipher.Decrypt( cipherText ) ) ;
}
return hash ;
}
/// <summary>
/// The core encrypt/decrypt/hash engine
/// </summary>
private class EncryptionEngine : IDisposable
{
/// <summary>
/// get an instance of this class
/// </summary>
/// <returns></returns>
public static EncryptionEngine GetInstance()
{
return new EncryptionEngine() ;
}
#region IDisposable Members
/// <summary>
/// Dispose of any unmanaged resources
/// </summary>
public void Dispose()
{
throw new NotImplementedException();
}
#endregion
/// <summary>
/// Encrypt a plaintext string
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
internal byte[] Encrypt( string plainText )
{
throw new NotImplementedException();
}
/// <summary>
/// Decrypt an encrypted string
/// </summary>
/// <param name="cipherText"></param>
/// <returns></returns>
internal string Decrypt( byte[] cipherText )
{
throw new NotImplementedException();
}
/// <summary>
/// Compute the secure hash of a string
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
internal byte[] ComputeSecureHash( string plainText )
{
throw new NotImplementedException();
}
}
}
}
Implementation of the internals of EncryptionEngine is left as an exercise for the reader.
You can take a look at the authentication of your application, and see from the source code how it authenticates the password. There you should see that the app is encrypting the password and comparing it with the encrypted value in the database. The encryption function there should be easy to reverse. A salt is not usually used along with encryption, it is used when generating a hash to protected against lookup attacks.
I don't think SQL can do decryption on AES128, not in a straightforward manner anyway. But you can write a simple .NET app using the standard APIs that will decrypt each password, hash it with the salt and write it back to the database.
The point with storing encrypted passwords is that they cannot be decrypted. The encryption is in fact made on some constant (+salt) using the password as the key.
So basically the goal has already been met, you cannot decrypt the "passwords" to get their clear text versions.
Someone else has already asked a somewhat similar question: Validate an Xml file against a DTD with a proxy. C# 2.0
Here's my problem: We have a website application that needs to use both internal and external resources.
We have a bunch of internal
webservices. Requests to the CANNOT
go through the proxy. If we try to, we get 404 errors since the proxy DNS doesn't know about our internal webservice domains.
We generate a
few xml files that have to be valid.
I'd like to use the provided dtd
documents to validate the xml. The
dtd urls are outside our network and
MUST go through the proxy.
Is there any way to validate via dtd through a proxy without using system.net.defaultproxy? If we use defaultproxy, the internal webservices are busted, but the dtd validation works.#
Here is what I'm doing to validate the xml right now:
public static XDocument ValidateXmlUsingDtd(string xml)
{
var xrSettings = new XmlReaderSettings {
ValidationType = ValidationType.DTD,
ProhibitDtd = false
};
var sr = new StringReader(xml.Trim());
XmlReader xRead = XmlReader.Create(sr, xrSettings);
return XDocument.Load(xRead);
}
Ideally, there would be some way to assign a proxy to the XmlReader much like you can assign a proxy to the HttpWebRequest object. Or perhaps there is a way to programatically turn defaultproxy on or off? So that I can just turn it on for the call to Load the Xdocument, then turn it off again?
FYI - I'm open to ideas on how to tackle this - note that the proxy is located in another domain, and they don't want to have to set up a dns lookup to our dns server for our internal webservice addresses.
Cheers,
Lance
Yes, you can fix this.
One option is to create your own resolver that handles the DTD resolution. It can use whatever mechanism it likes, including employing a non-default proxy for outbound communications.
var xmlReaderSettings = new XmlReaderSettings
{
ProhibitDtd = false,
ValidationType = ValidationType.DTD,
XmlResolver = new MyCustomDtdResolver()
};
In the code for MyCustomDtdResolver, you'd specify your desired proxy setting. It could vary depending on the DTD.
You didn't specify, but if the DTDs you are resolving against are fixed and unchanging, then Silverlight and .NET 4.0 have a built-in resolver that does not hit the network (no proxy, no http comms whatsoever). It's called XmlPreloadedResolver. Out of the box it knows how to resolve RSS091 and XHTML1.0. If you have other DTDs, including your own custom DTDs, and they are fixed or unchanging, you can load them into this resolver and use it at runtime, and completely avoid HTTP comms and the proxy complication.
More on that.
If you are not using .NET 4.0, then you can build a "no network" resolver yourself. To avoid the W3C traffic limit, I built a custom resolver myself, for XHTML, maybe you can re-use it.
See also, a related link.
For illustration, here's the code for ResolveUri in a custom Uri resolver.
/// <summary>
/// Resolves URIs.
/// </summary>
/// <remarks>
/// <para>
/// The only Uri's supported are those for W3C XHTML 1.0.
/// </para>
/// </remarks>
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
if (baseUri == null)
{
if (relativeUri.StartsWith("http://"))
{
Trace(" returning {0}", relativeUri);
return new Uri(relativeUri);
}
// throw if Uri scheme is unknown/unhandled
throw new ArgumentException();
}
if (relativeUri == null)
return baseUri;
// both are non-null
var uri = baseUri.AbsoluteUri;
foreach (var key in knownDtds.Keys)
{
// look up the URI in the table of known URIs
var dtdUriRoot = knownDtds[key];
if (uri.StartsWith(dtdUriRoot))
{
string newUri = uri.Substring(0,dtdUriRoot.Length) + relativeUri;
return new Uri(newUri);
}
}
// must throw if Uri is unknown/unhandled
throw new ArgumentException();
}
here's the code for GetEntity
/// <summary>
/// Gets the entity associated to the given Uri, role, and
/// Type.
/// </summary>
/// <remarks>
/// <para>
/// The only Type that is supported is the System.IO.Stream.
/// </para>
/// <para>
/// The only Uri's supported are those for W3C XHTML 1.0.
/// </para>
/// </remarks>
public override object GetEntity(Uri absoluteUri, string role, Type t)
{
// only handle streams
if (t != typeof(System.IO.Stream))
throw new ArgumentException();
if (absoluteUri == null)
throw new ArgumentException();
var uri = absoluteUri.AbsoluteUri;
foreach (var key in knownDtds.Keys)
{
if (uri.StartsWith(knownDtds[key]))
{
// Return the stream containing the requested DTD.
// This can be a FileStream, HttpResponseStream, MemoryStream,
// or whatever other stream you like. I used a Resource stream
// myself. If you retrieve the DTDs via HTTP, you could use your
// own IWebProxy here.
var resourceName = GetResourceName(key, uri.Substring(knownDtds[key].Length));
return GetStreamForNamedResource(resourceName);
}
}
throw new ArgumentException();
}
The full working code for my custom resolver is available.
If your resolver does network comms, then for a general solution you may want to override the Credentials property.
public override System.Net.ICredentials Credentials
{
set { ... }
}
Also, you may want to expose a Proxy property. Or not. As I said above, you may want to automatically determine the proxy to use, from the DTD URI.