I currently have an application developed in C# that helps me in managing permissions on our Share-point 2013 site. Recently, I learned we may be loosing our local instance and moving to another instance that's behind a cac enforced IIS. I have converted one of my test sites to require certificates and have tried several way to send the cert to the IIS server but I still get
"The remote server returned and error: (403) Forbidden.
Below is a few things I have tried.
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
handler.ClientCertificates.Add(pki.GetClientCertificate());
handler.UseProxy = false;
using (var client = new HttpClient(handler))
{
context connection code here
}
the pki.GetClientCertificate is a method, I made that returns a selected certificate in this case my cac cert. Its funny that SharePoint designer connects without issue or prompt. Any help on this matter would be much appreciated.
Just to add some more things I have tried
context.Credentials = new SharePointOnlineCredentials(uli.username, uli.password);
the uli username is the certificate converted to username I have a class that dose the conversion. the password is the pin converted to a secure string. I get the same message even when adding the credentials to the context.
I found a workable but slow solution here:
http://sharepoint.findincity.net/view/635399286724222582121618/ssl-certificate-error-when-using-client-object-model
The only issue with this is every time I call the context I have to send the certificate chain. One thing I changed from this users code is the following.
static void context_ExecutingWebRequest(object sender, WebRequestEventArgs e)
{
IntPtr ptr = IntPtr.Zero;
X509Certificate2 certificate = null;
X509Certificate t = null;
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
// Nothing to do if no cert found.
HttpWebRequest webReq = e.WebRequestExecutor.WebRequest;
//webReq.Proxy = new WebProxy("http://[ProxyAddress]");
//Specify a proxy address if you need to
// X509Certificate cert = pki.GetClientCertificate();
foreach (X509Certificate c in store.Certificates)
{
webReq.ClientCertificates.Add(c);
}
}
I just dumped all my certificates into the request because I didn't want to have a prompt every time I clicked something. So if anyone has a more efficient way to do this let me know.
The code below shows the use of the clientcontext and how it validates your cert
using (context = new ClientContext(siteurl))
{
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender1, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
bool validationResult = true;
return validationResult;
};
context.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>(context_ExecutingWebRequest);
//add all your context commands below this line
}
Related
Help please, I'm building a .NET Core API with ionic front end. I want to use ASPNET Core Identity so I was more or less following this example
https://identityserver4.readthedocs.io/en/release/quickstarts/6_aspnet_identity.html
here is what I have in Startup.cs
// Adds IdentityServer
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients(Configuration))
.AddAspNetIdentity<ApplicationUser>();
and
app.UseIdentity();
app.UseIdentityServer();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = API_address,
RequireHttpsMetadata = false,
ApiName = "myAPIs"
});
and in my Config.cs file for in memory configurations I have
public class Config
{
// scopes define the resources in your system
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
// scopes define the API resources in your system
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource(
"myAPIs", // Api resource name
"My API Set #1", // Display name
new[] { JwtClaimTypes.Name, JwtClaimTypes.Role }) // Claims to be included in access token
};
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients(IConfigurationRoot configuration)
{
return new List<Client>
{
new Client
{
ClientId = "myClient",
ClientName = "My Custom Client",
AllowedCorsOrigins = new List<string>
{
"whateverINeedHere"
},
AccessTokenLifetime = 60 * 60 * 24,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
RequireClientSecret = false,
AccessTokenType = AccessTokenType.Jwt,
AllowedScopes =
{
"myAPIs"
}
}
};
}
}
Now the problem is that when I test this locally, everything works just fine.
I hit the /connect/token endpoint, I get a token response, hit the controller that needs token authorization and my claims are there. But when I deploy it to Azure, when I want to use the token (issued from that environment) I get 401 Unauthorized with response header invalid_token "The issuer is invalid". I've Googled, but people get invalid tokens with signature problems, not issuer. I've never used identity server before and to me this looks like it's some configuration problem. I have compared tokens I get from identity server on jwt.io, they look exactly the same, only difference being the issuer localhost -> myAPIAddress.
Can someone point me to the right direction?
This smells like it could be the temporary signing credentials. I also ran into problems when deploying to Azure when my cert wasn't loading.
I suggest you create a self signed cert and add it to azure using the following instructions. (Note this can be done in the new portal).
https://azure.microsoft.com/en-us/blog/using-certificates-in-azure-websites-applications/
REMEMBER: Make sure you add the WEBSITE_LOAD_CERTIFICATES application setting!
Also for your benefit, here's the code I use to load the cert in my startup.cs. I keep a copy of the cert in the repository so I can load it from disk as a fallback (when I'm on my dev machine).
X509Certificate2 cert = null;
using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
// Replace below with your cert's thumbprint
"A9781679661914B7539BE020EE9C4F6880579F42",
false);
// Get the first cert with the thumbprint
if (certCollection.Count > 0)
{
cert = certCollection[0];
// Use certificate
Log.Logger.Information($"Successfully loaded cert from registry: {cert.FriendlyName}");
}
}
// Fallback to local file for development
if (cert == null)
{
cert = new X509Certificate2(Path.Combine(_env.ContentRootPath, "myauth.pfx"), "mypassword");
Log.Logger.Information($"Falling back to cert from file. Successfully loaded : {cert.FriendlyName}");
}
services.AddIdentityServer()
.AddSigningCredential(cert)
Could be you've got an SSL/TLS issue between client and IdentityServer, are you able to view logged exceptions from IdentityServer itself? You may see something like:
"... Could not establish trust relationship for the SSL/TLS..."
If you're running IdentityServer on HTTPS you need to make sure you've got its domain/sub-domain in your certificate.
Either way, IdentityServer logs lots of useful info so turn on logging and check out what it says, that should point you in the right direction.
Following is my code for the secure ssl server. I have created a keystore "server" and it has the key pair generated with passwords.
public static void main(String[] args) throws Exception {
Server server = new Server();
HttpConfiguration https_config = new HttpConfiguration();
https_config.setSecureScheme("https");
https_config.setSecurePort(8443);
https_config.addCustomizer(new SecureRequestCustomizer());
https_config.setSendServerVersion(true);
File keystoreFile = new File("server");
System.out.print(keystoreFile.getAbsolutePath());
SslContextFactory sslContextFactory = new SslContextFactory();
if (keystoreFile.exists())
{
sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
sslContextFactory.setTrustStorePath(keystoreFile.getAbsolutePath());
sslContextFactory.setKeyStorePassword("secret");
sslContextFactory.setKeyManagerPassword("secret");
sslContextFactory.setTrustStorePassword("secret");
sslContextFactory.setExcludeCipherSuites(
"SSL_RSA_WITH_DES_CBC_SHA",
"SSL_DHE_RSA_WITH_DES_CBC_SHA",
"SSL_DHE_DSS_WITH_DES_CBC_SHA",
"SSL_RSA_EXPORT_WITH_RC4_40_MD5",
"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
}
ServerConnector https =
new ServerConnector(server,
new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(https_config));
https.setPort(8443);
server.setConnectors(new Connector[] { https});
ServletContextHandler scHandler = new ServletContextHandler(server,"/");
scHandler.addServlet(Testpage1.class, "/test");
server.setHandler(scHandler);
server.start();
}
}
when I tried to connect to using the https://localhost:8443/ or https://localhost:8443/test it gives me "web page not available error" and with curl it gives me "curl: (35) Unknown SSL protocol error in connection to localhost:8443"
Could some one guide me to debug this issue.
I decided to post the question after a full day of debugging and trials and but with a little suggestion from a friend I manage to solve the issue by updating the keystore. So the issue I was having was due to the keys I have generated int he key store. It seems you need to use RSA algorithm not the EC algorithm.
I try to reach my STS to request a token. The code is based on a blog post by #leastprivilege : WCF and Identity in .NET 4.5: External Authentication with WS-Trust. I use the explicit approach (by code).
private static SecurityToken RequestSecurityToken()
{
// set up the ws-trust channel factory
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(
SecurityMode.TransportWithMessageCredential),
"https://federation.mydomain/adfs/services/trust/mex") { TrustVersion = TrustVersion.WSTrust13 };
//factory.Credentials.SupportInteractive = false;
factory.Credentials.UserName.UserName = "user-pcote";
factory.Credentials.UserName.Password = "123456";
// create token request
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Symmetric,
AppliesTo = new EndpointReference("https://myRP/")
};
var channel = factory.CreateChannel();
return channel.Issue(rst);
}
I can see the XML when copying the STS endpoint adress in my browser, therefore the federation server is reachable. But I always get an internal server error (500) as soon as I issue the token request. Does anybody have an idea what could be my problem here.
Finally managed to get it working by changing the KeyType to KeyTypes.Bearer (since there's no certificate applied to the RP in AD FS). I based myseflf on this website that gives a good explanations on how it all relates :
http://blog.skadefro.dk/2011/09/claimsbased-authentication-and-wcf.html
if we look in Microsoft.IdentityModel.SecurityTokenService.KeyTypes we
see we can use Asymmetric, Symmetric or Bearer. Tons of post out there
about this.
If you use Asymmetric you as requestor need to supply a key to encrypt
the claims with. ( set "UseKey” )
If you use Symmetric the identity provider have all ready been told
what certificate to use, to encrypt the claims with.
If you choose Bearer. The token get signed, but claims will not be
encrypted. If a token signing certificate have been assigned on the
Relying Party, claims will simply not be included at all.
When you request a token, the token gets signed (not encrypted) with a
certificate installed on the Identity Provider ( ADFS ). If you add a
certificate on a Relying Party Trust (RP) on the ADFS server, the
claims inside the token gets encrypted with with that certificate.
Only host/applications that have access to the private key of that
certificate can now decrypt the token and read the claims. You don’t
need to read the claims in order to authenticate your self. For
instance if you have a WCF Service you want to call from within an
application. You can from within that application still request a
token from the ADFS server and then access the WCF service with that
Token. As long as the WCF service have access to the private key and
can read the claims, your application don’t need it.
private static SecurityToken RequestSecurityToken()
{
var binding = new UserNameWSTrustBinding(
SecurityMode.TransportWithMessageCredential);
var factory = new WSTrustChannelFactory(
binding,
new EndpointAddress(new Uri("<your_adfs_uri>/adfs/services/trust/13/usernamemixed"), EndpointIdentity.CreateSpnIdentity("host/your.spn.com"))) { TrustVersion = TrustVersion.WSTrust13 };
factory.Credentials.UserName.UserName = "username";
factory.Credentials.UserName.Password = "password";
// create token request
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new EndpointReference(<uri_of_your_relying_party>)
};
var channel = factory.CreateChannel();
try
{
var response = channel.Issue(rst);
return response ;
}
catch (Exception e)
{
var message = e.Message;
return null;
}
}
I managed to find the right endpoint (which was /adfs/services/trust/13/usernamemixed) but now I get the following error :
ID4007: The symmetric key inside the requested security token must be encrypted
I created Traffic Manager throught its REST API using 2011-10-01 MS verion.
Resources I followed -
Create Profile -
http://msdn.microsoft.com/en-us/library/windowsazure/hh758254.aspx
Create Definition -
http://msdn.microsoft.com/en-us/library/windowsazure/hh758257.aspx
Traffic Manager got created successfully. All happies.
But after 30mins of time, traffic manager is going to INACTIVE status and all its endpoints are GONE. It shows there are no endpoints associated with it.
I am not sure what is happening around. Is it Azure problem? or is it REST API problem? or is it my way of creating Traffic manager problem.
PS - I followed this sample for making REST API calls - http://msdn.microsoft.com/en-us/library/windowsazure/gg651127.aspx
Any help would be highly appreciated.
UPDATE1
Parameters
SubscriptionID - a Valid GUID from publishsettings
Certificate - I cross checked a valid certificate present in the local cert store
endpoint1 domain name - JASH13.CLOUDAPP.NET
endpoint2 domain name - JASH23.CLOUDAPP.NET
There is no error at REST API calls level. Everything worked seamlessly.
Profile Creation -
// X.509 certificate variables.
X509Store certStore = null;
X509Certificate2Collection certCollection = null;
X509Certificate2 certificate = null;
// Request and response variables.
HttpWebRequest httpWebRequest = null;
HttpWebResponse httpWebResponse = null;
// Stream variables.
Stream responseStream = null;
StreamReader reader = null;
// URI variable.
Uri requestUri = null;
// The thumbprint for the certificate. This certificate would have been
// previously added as a management certificate within the Windows Azure management portal.
string thumbPrint = CertificateThumbprint;
// Open the certificate store for the current user.
certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
// Find the certificate with the specified thumbprint.
certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
thumbPrint,
false);
// Close the certificate store.
certStore.Close();
// Check to see if a matching certificate was found.
if (0 == certCollection.Count)
{
throw new Exception("No certificate found containing thumbprint " + thumbPrint);
}
// A matching certificate was found.
certificate = certCollection[0];
// Create the request.
requestUri = new Uri("https://management.core.windows.net/"
+ SubscriptionId
+ "/services/WATM/profiles");
httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(requestUri);
// Add the certificate to the request.
httpWebRequest.ClientCertificates.Add(certificate);
httpWebRequest.Method = "POST";
httpWebRequest.Headers.Add("x-ms-version", "2011-10-01");
string str = #"<Profile xmlns=""http://schemas.microsoft.com/windowsazure""><DomainName>" + ProfileDomain + "</DomainName><Name>" + ProfileName + "</Name></Profile>";
byte[] bodyStart = System.Text.Encoding.UTF8.GetBytes(str.ToString());
Stream dataStream = httpWebRequest.GetRequestStream();
dataStream.Write(bodyStart, 0, str.ToString().Length);
// Make the call using the web request.
httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
// Parse the web response.
responseStream = httpWebResponse.GetResponseStream();
reader = new StreamReader(responseStream);
// Close the resources no longer needed.
httpWebResponse.Close();
responseStream.Close();
reader.Close();
Definition Creation-
// X.509 certificate variables.
X509Store certStore = null;
X509Certificate2Collection certCollection = null;
X509Certificate2 certificate = null;
// Request and response variables.
HttpWebRequest httpWebRequest = null;
HttpWebResponse httpWebResponse = null;
// Stream variables.
Stream responseStream = null;
StreamReader reader = null;
// URI variable.
Uri requestUri = null;
// The thumbprint for the certificate. This certificate would have been
// previously added as a management certificate within the Windows Azure management portal.
string thumbPrint = CertificateThumbprint;
// Open the certificate store for the current user.
certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
// Find the certificate with the specified thumbprint.
certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
thumbPrint,
false);
// Close the certificate store.
certStore.Close();
// Check to see if a matching certificate was found.
if (0 == certCollection.Count)
{
throw new Exception("No certificate found containing thumbprint " + thumbPrint);
}
// A matching certificate was found.
certificate = certCollection[0];
// Create the request.
requestUri = new Uri("https://management.core.windows.net/"
+ SubscriptionId
+ "/services/WATM/profiles/" + ProfileName + "/definitions");
httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(requestUri);
// Add the certificate to the request.
httpWebRequest.ClientCertificates.Add(certificate);
httpWebRequest.Method = "POST";
httpWebRequest.Headers.Add("x-ms-version", "2011-10-01");
string str = #"<Definition xmlns=""http://schemas.microsoft.com/windowsazure""><DnsOptions><TimeToLiveInSeconds>300</TimeToLiveInSeconds></DnsOptions><Monitors><Monitor><IntervalInSeconds>30</IntervalInSeconds><TimeoutInSeconds>10</TimeoutInSeconds><ToleratedNumberOfFailures>3</ToleratedNumberOfFailures><Protocol>HTTP</Protocol><Port>80</Port><HttpOptions><Verb>GET</Verb><RelativePath>/</RelativePath><ExpectedStatusCode>200</ExpectedStatusCode></HttpOptions></Monitor></Monitors><Policy><LoadBalancingMethod>RoundRobin</LoadBalancingMethod><Endpoints><Endpoint><DomainName>" + PrimaryService + "</DomainName><Status>Enabled</Status></Endpoint><Endpoint><DomainName>" + SecondaryService + "</DomainName><Status>Enabled</Status></Endpoint></Endpoints></Policy></Definition>";
byte[] bodyStart = System.Text.Encoding.UTF8.GetBytes(str.ToString());
Stream dataStream = httpWebRequest.GetRequestStream();
dataStream.Write(bodyStart, 0, str.ToString().Length);
// Make the call using the web request.
httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
// Parse the web response.
responseStream = httpWebResponse.GetResponseStream();
reader = new StreamReader(responseStream);
// Close the resources no longer needed.
httpWebResponse.Close();
responseStream.Close();
reader.Close();
UPDATE2
Once the TM went into Inactive State, I checked the profile definition using REST API. In there I was not able to find any endpoints. They are missing.
<Definitions xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Definition>
<DnsOptions>
<TimeToLiveInSeconds>300</TimeToLiveInSeconds>
</DnsOptions>
<Status>Enabled</Status>
<Version>1</Version>
<Monitors>
<Monitor>
<IntervalInSeconds>30</IntervalInSeconds>
<TimeoutInSeconds>10</TimeoutInSeconds>
<ToleratedNumberOfFailures>3</ToleratedNumberOfFailures>
<Protocol>HTTP</Protocol>
<Port>80</Port>
<HttpOptions>
<Verb>GET</Verb>
<RelativePath>/</RelativePath>
<ExpectedStatusCode>200</ExpectedStatusCode>
</HttpOptions>
</Monitor>
</Monitors>
<Policy>
<LoadBalancingMethod>Performance</LoadBalancingMethod>
<Endpoints/>
<MonitorStatus>Inactive</MonitorStatus>
</Policy>
</Definition>
</Definitions>
UPDATE3
This sporadic behavior is ONLY happening with the specific cloud services and TM profile/definitiona. When I create new set of cloud services and TM profile, then everything seems to be working fine. I tested this multiple times. So the only problem is with following parameters.
endpoint1 domain name - JASH13.CLOUDAPP.NET
endpoint2 domain name -JASH23.CLOUDAPP.NET
TM Domain - ramitm.trafficmanager.net
TM profilename - ramitm
This seems like some DNS Problems for very fast REST API Operations. I was not able to get the crux of the problem, but this is how I solved it.
Previously I was getting this problem for this patter - Create -> Delete -> Re-Create
Now I made it this way - Create -> Delete -> Delay -> Re-Create
I think by introducing delay component, I am giving enough time for Azure to settle down all the DNS and infrastructure and there by update them. So after introducing delay, I was not experiencing the problem. Delay can be 5 - 10 mins.
I have a MOSS 2007 farm that requires a client certificate to access. I have written several methods that use the built in SharePoint services to retrieve data from the site. However, outside of my local test environment everything requires a client cert to access the services.
My method for retrieving the cert is:
private static X509Certificate2 GetCertCreds()
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectKeyIdentifier, "SiteIdentityCertificateSerialNumber", true);
Assert.IsNotNull(certs);
return certs[0];
}
finally
{
store.Close();
}
}
Then in use I have to have something LIKE:
using (ListsServiceProxy.Lists service = new ListsServiceProxy.Lists())
{
service.Crendentials = GetCredentials();
XmlNode idResultsNode = service.GetListItems(documentLibraryName, null, queryNode, viewNode, "1", optionNode, null);
}
This is not compiling because of the type mismatch. Am I going about this the wrong way? If not is there a way to use the certificate as the services credentials? I think in end what I'm trying to do is convert an X509Certificate2 to ICredentials. Any help or advice would be greatly appreciated.
It turns out I had it all wrong. To correctly do this I replaced:
service.Credentials = GetCertCreds()
with
service.ClientCertificates.Add(GetCretCreds());
Adding the certificate into the service's client certificates collection is all that needed to happen.