Skip to content

Commit

Permalink
Add support for Alipay certificate signing.
Browse files Browse the repository at this point in the history
  • Loading branch information
BuknSS committed Oct 30, 2024
1 parent 4c441bd commit 3e5e459
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ protected override async Task<OAuthTokenResponse> ExchangeCodeAsync([NotNull] OA
["timestamp"] = TimeProvider.GetUtcNow().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
["version"] = "1.0",
};
if (AlipayCertificationManager.Enable)
{
tokenRequestParameters["app_cert_sn"] = AlipayCertificationManager.AppCertSN;
tokenRequestParameters["alipay_root_cert_sn"] = AlipayCertificationManager.RootCertSN;
}

tokenRequestParameters.Add("sign", GetRSA2Signature(tokenRequestParameters));

// PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see BuildChallengeUrl
Expand Down Expand Up @@ -107,6 +113,12 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
["timestamp"] = TimeProvider.GetUtcNow().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
["version"] = "1.0",
};
if (AlipayCertificationManager.Enable)
{
parameters["app_cert_sn"] = AlipayCertificationManager.AppCertSN;
parameters["alipay_root_cert_sn"] = AlipayCertificationManager.RootCertSN;
}

parameters.Add("sign", GetRSA2Signature(parameters));

var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, parameters);
Expand Down
24 changes: 23 additions & 1 deletion src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,31 @@ public AlipayAuthenticationOptions()
ClaimActions.MapCustomJson(System.Security.Claims.ClaimTypes.NameIdentifier, user => user.GetString(NameIdentifierKey));
}

public override void Validate()
{
base.Validate();

if (!string.IsNullOrEmpty(AppCertPath) && !string.IsNullOrEmpty(RootCertPath))
{
try
{
AlipayCertificationManager.AppCertSN = AlipayCertificationManager.GetCertSN(AppCertPath);
AlipayCertificationManager.RootCertSN = AlipayCertificationManager.GetRootCertSN(RootCertPath);
AlipayCertificationManager.Enable = true;
}
catch (Exception ex)
{
throw new ArgumentException($"The '{nameof(AppCertPath)}' and '{nameof(RootCertPath)}' options must be set to the correct certificate files.", ex);
}
}
}

/// <summary>
/// Alipay user system internal identifier, which will no longer be open independently in the future and will be replaced by open_id. Currently the default is user_id
/// See https://opendocs.alipay.com/mini/0ai2i6?pathHash=13dd5946
/// </summary>
public string NameIdentifierKey { get; set; } = "user_id";

public string? AppCertPath { get; set; }

public string? RootCertPath { get; set; }
}
61 changes: 61 additions & 0 deletions src/AspNet.Security.OAuth.Alipay/AlipayCertificationManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/

using System.Numerics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace AspNet.Security.OAuth.Alipay;

/// <summary>
/// This class of code refers to the Alipay official SDK code.
/// See https://github.com/alipay/alipay-sdk-net-all/blob/1b7b73909954b107bddb6476dec68aafcc3f16e9/v2/AlipaySDKNet.Standard/Util/AntCertificationUtil.cs
/// See https://opendocs.alipay.com/common/056zub?pathHash=91c49771
/// </summary>
internal static class AlipayCertificationManager
{
public static bool Enable { get; set; }

public static string AppCertSN { get; set; } = string.Empty;

public static string RootCertSN { get; set; } = string.Empty;

internal static string GetCertSN([NotNull] string certFilePath)
{
using var cert = X509Certificate.CreateFromCertFile(certFilePath);
return GetCertSN(cert);
}

internal static string GetCertSN([NotNull] X509Certificate cert)
{
var issuerDN = cert.Issuer.Replace(", ", ",", StringComparison.InvariantCulture);
var input = issuerDN + new BigInteger(cert.GetSerialNumber());
#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
var certSN = Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(input))).ToLowerInvariant();
#pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms
return certSN;
}

internal static string GetRootCertSN([NotNull] string rootCertPath, [NotNull] string signType = "RSA2")
{
var certSNs = new List<string>();
var certCollection = new X509Certificate2Collection();
certCollection.ImportFromPemFile(rootCertPath);

foreach (var cert in certCollection)
{
if ((signType.StartsWith("RSA", StringComparison.Ordinal) && cert.SignatureAlgorithm.Value?.StartsWith("1.2.840.113549.1.1", StringComparison.Ordinal) == true) ||
(signType.Equals("SM2", StringComparison.Ordinal) && cert.SignatureAlgorithm.Value?.StartsWith("1.2.156.10197.1.501", StringComparison.Ordinal) == true))
{
certSNs.Add(GetCertSN(cert));
}
}

var rootCertSN = string.Join('_', certSNs);
return rootCertSN;
}
}

0 comments on commit 3e5e459

Please sign in to comment.