我正在创建一个API,以通过私有应用程序登录Xero,因此我按照他们的说明和Xero API文档查看其工作原理。链接here。
因此,我创建了一个应用程序,并使用OpenSSL生成了公共证书(.cer文件),还为其创建了“ .pfx文件”,并将该文件附加到Notes实体中的Dynamics CRM中。
现在,当我运行C#代码以使用同一对公钥/私钥对登录时,它可以按预期工作。 (从CRM中检索到私钥/公钥对),并按预期方式将其传递给登录代码(代码已粘贴在下面)
下一步是使用C#以编程方式创建这些公钥/私钥对,然后使用下面的代码创建它。
// Generate a RSA Key
RSA RSAKey = RSA.Create(1024);
// Creating Certificate Request with relevant parameters
var req = new CertificateRequest(subjectNameDN, RSAKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// Creating self-signed certificate with an expiry defined in app settings
X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(expiryDurationInYears));
subjectNameDN变量以字符串格式包含我的OrganizationName,OrganizationUnitName和CommonName。
此步骤之后,我要做的是创建一个.pfx文件和一个.zip文件(zip文件中包含.cert文件),并将这两个文件都附加到Dynamics CRM中的注释记录中。
下面是代码
创建.pfx文件并附加到CRM中的注释:
Entity annotationPfx = new Entity("annotation");
annotationPfx.Attributes["subject"] = "public_privatekey pfx";
annotationPfx.Attributes["filename"] = "public_privatekey.pfx";
annotationPfx.Attributes["documentbody"] = Convert.ToBase64String(cert .Export(X509ContentType.Pfx, myPassword));
annotationPfx.Attributes["mimetype"] = "application/x-pkcs12";
annotationPfx.Attributes["isdocument"] = true;
_crmServiceClient.Create(annotationPfx);
使用.pfx文件创建zip文件,并附加到CRM中的注释:
var certVal = "-----BEGIN CERTIFICATE-----" + Environment.NewLine + Convert.ToBase64String(cert .Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks) + Environment.NewLine + "-----END CERTIFICATE-----";
byte[] certificateInBytes = System.Text.Encoding.UTF8.GetBytes(certVal);
byte[] compressedCertInBytes;
string certFileName = "MyCertificate.cer";
using (var outStream = new MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry(certFileName, CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(certificateInBytes))
{
fileToCompressStream.CopyTo(entryStream);
}
}
compressedCertInBytes = outStream.ToArray();
}
Entity annotationZip = new Entity("annotation");
annotationZip.Attributes["subject"] = "publickey zip";
annotationZip.Attributes["filename"] = "publickey.zip";
annotationZip.Attributes["documentbody"] = Convert.ToBase64String(compressedCertInBytes);
annotationZip.Attributes["mimetype"] = "application/zip";
annotationZip.Attributes["isdocument"] = true;
然后,我要做的是,下载zip文件,解压缩,然后将其粘贴到Xero的Private Application中。
然后,我运行我的代码以登录Xero(粘贴在下面)
var baseApiUrl = "https://api.xero.com";
var consumerKey = myConsumerKey;
var privateConsumer = new Consumer(consumerKey, consumerSecret);
certificate = new X509Certificate2(Convert.FromBase64String(retrieveBase64FromCRM),
myPassword,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
var privateAuthenticator = new PrivateAuthenticator(certificate);
var privateApplicationSettings = new ApplicationSettings
{
BaseApiUrl = baseApiUrl,
Consumer = privateConsumer,
Authenticator = privateAuthenticator
};
ApplicationSettings applicationSettings = privateApplicationSettings;
if (applicationSettings.Authenticator is IAuthenticator)
{
IXeroCoreApi myXeroApi = new XeroCoreApi(applicationSettings.BaseApiUrl, applicationSettings.Authenticator as IAuthenticator,
applicationSettings.Consumer, User(), new DefaultMapper(), new DefaultMapper());
}
string getXeroOrgName = myXeroApi != null && !string.IsNullOrEmpty(myXeroApi.Organisation.Name.ToString()) ? myXeroApi.Organisation.Name.ToString() : string.Empty;
此代码是发生故障的地方。我收到以下错误。
我传递参数'X509KeyStorageFlags.MachineKeySet |时出错。 X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable”以获取证书
我收到错误访问拒绝消息。
堆栈跟踪:-
System.Security.Cryptography.CryptographicException: Access denied.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[] rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)
at myFunctionName(Dictionary`2 myParameters)
没有这些参数,我得到的错误是
堆栈跟踪:
System.Security.Cryptography.CryptographicException: Invalid provider type specified.
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
at Xero.Api.Infrastructure.ThirdParty.Dust.RsaSha1.Sign(SHA1CryptoServiceProvider hash)
at Xero.Api.Infrastructure.ThirdParty.Dust.RsaSha1.SignCore(String baseString)
at Xero.Api.Infrastructure.OAuth.Signing.RsaSha1Signer.CreateSignature(X509Certificate2 certificate, IToken token, Uri uri, String verb, String verifier, Boolean renewToken, String callback)
at Xero.Api.Example.Applications.Private.PrivateAuthenticator.GetSignature(IConsumer consumer, IUser user, Uri uri, String verb, IConsumer consumer1)
at Xero.Api.Infrastructure.Http.HttpClient.CreateRequest(String endPoint, String method, String accept, String query)
at Xero.Api.Infrastructure.Http.HttpClient.Get(String endpoint, String query)
at Xero.Api.Infrastructure.Http.XeroHttpClient.Get[TResult,TResponse](String endPoint)
at Xero.Api.Common.XeroReadEndpoint`3.Get(String endpoint, String child)
at Xero.Api.Common.XeroReadEndpoint`3.Find()
at Xero.Api.Core.XeroCoreApi.get_Organisation()
at myFunctionName(Dictionary`2 myParameters)
但是令我惊讶的是,当我在Azure上托管此API时,它可以按预期运行,没有任何问题,并且能够登录Xero,但无法在本地计算机上对其进行调试。
如果我使用通过OpenSSL创建的证书的值,则可以调试代码,但是如果我使用以编程方式创建的证书的值,则会出错。
任何帮助,将不胜感激。
答案 0 :(得分:1)
拒绝访问是因为您试图将私钥保存到计算机密钥库(MachineKeySet)中,并且不是管理员。
无效的提供程序类型是因为PFX中的私钥没有表示密钥存储提供程序,因此打开它时,将其保存在CNG(vs CAPI)中,并且Xero库使用cert.PrivateKey而不是cert.GetRSAPrivateKey()。
如果您使用的是Windows,则可以通过在CAPI中生成原始密钥来解决此问题:
CspParameters cspParams = new CspParameters
{
KeyContainerName = Guid.NewGuid().ToString(),
};
using (var rsaCsp = new RSACryptoServiceProvider(2048, cspParams))
{
rsaCsp.PersistKeyInCsp = false;
// CertificateRequest stuff here
}
(或类似的方式,通过电话提交答案)
答案 1 :(得分:-1)
ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };
以上行将帮助您解决问题
在“尝试捕获”块中,我按如下方式使用了它
try
{
ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };
WebProxy proxy = new WebProxy("proxy.xyz.local", 81) { UseDefaultCredentials = true };
WebRequest request = WebRequest.Create(globaConfigStatciValues.Url);
request.Proxy = proxy;
request.Method = "GET";
request.Credentials = new NetworkCredential(globaConfigStatciValues.username,
globaConfigStatciValues.password);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
try
{
if (response.StatusCode == HttpStatusCode.OK)
{
isConnectionSucessfull = true;
tracing.Trace($" Resposne is correct {response.StatusCode}");
TracingMessage += $"Resposne is correct {response.StatusCode} \n";
}
else
{
TracingMessage += $"Response from connecting to API {response.StatusCode} \n";
tracing.Trace($"Response from connecting to API {response.StatusCode}");
}
}
catch (Exception e)
{
TracingMessage += $" In catch block {e} \n";
tracing.Trace($" In catch block {e}");
createLogRecord( e.StackTrace,TracingMessage);
// throw new Exception($"There was an issue with connecting to API {e.Message}");
}
}
}
catch (Exception e)
{
TracingMessage += $" In catch block of PerformGetMethodtoApi {e} \n";
tracing.Trace($" In catch block of PerformGetMethodtoApi {e}");
createLogRecord( e.StackTrace, TracingMessage);
// throw new Exception($"There was an issue with connecting to API {e.Message}");
}