ASP .Net Core 上的 RSA 和 SignedCms;需要多平台方法
RSA and SignedCms on ASP .Net Core; multiplatform approach needed
我正在尝试找到一种以适用于多平台的方式对 APK SF 文件进行证书签名的方法。目前没有成功;解释我在做什么。
签名(两者通用)
我正在使用发行者和序列号进行签名:
private static byte[] GetSigned(byte[] sfData, X509Certificate cert){
X509Certificate2 certificate = new X509Certificate2(cert.GetEncoded());
RSA rsaPriv = Certificate.ToRSA(cert.KeyPair.Private as RsaPrivateCrtKeyParameters);
X509Certificate2 certWithKey = RSACertificateExtensions.CopyWithPrivateKey(certificate, rsaPriv);
ContentInfo content = new ContentInfo(sfData);
SignedCms signedCms = new SignedCms(content, true);
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certWithKey);
signedCms.ComputeSignature(signer);
return signedCms.Encode();
}
Windows解法
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
{
return CreateRSAProvider(ToRSAParameters(privKey));
}
private static RSA CreateRSAProvider(RSAParameters rp)
{
CspParameters csp = new CspParameters
{
KeyContainerName = string.Format("BouncyCastle-{0}", Guid.NewGuid()),
Flags = CspProviderFlags.UseMachineKeyStore
};
// This is a workaround to fallback to user keystore while not machine is available;
// as otherwise it's impossible having something working on Azure and locally.
// It's more a bug of this cryptography stuff on ASP .Net core..
RSACryptoServiceProvider rsaCsp;
try
{
rsaCsp = new RSACryptoServiceProvider(csp);
}catch(Exception ex)
{
csp.Flags = CspProviderFlags.NoFlags;
rsaCsp = new RSACryptoServiceProvider(csp);
}
rsaCsp.ImportParameters(rp);
return rsaCsp;
}
private static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
RSAParameters rp = new RSAParameters();
rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
rp.P = privKey.P.ToByteArrayUnsigned();
rp.Q = privKey.Q.ToByteArrayUnsigned();
rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);
return rp;
}
Linux解法
在这个 之后,我使用了 RSA;喜欢:
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
{
var rp = ToRSAParameters(privKey);
return RSA.Create(rp);
}
在 Windows 上使用 Linux 解决方案时,出现以下异常
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] System.ArgumentException : The CNG key handle being opened was detected to be ephemeral, but the EphemeralKey open option was not specified.
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] Parameter name: keyHandleOpenOptions
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] Stack Trace:
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.CngKey.Open(SafeNCryptKeyHandle keyHandle, CngKeyHandleOpenOptions keyHandleOpenOptions)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKey[T](X509Certificate2 certificate, Boolean silent, Boolean preferNCrypt)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKeyForSigning[T](X509Certificate2 certificate, Boolean silent)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.CmsSignature.RSAPkcs1CmsSignature.Sign(ReadOnlySpan`1 dataHash, HashAlgorithmName hashAlgorithmName, X509Certificate2 certificate, Boolean silent, Oid& signatureAlgorithm, Byte[]& signatureValue)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.CmsSignature.Sign(ReadOnlySpan`1 dataHash, HashAlgorithmName hashAlgorithmName, X509Certificate2 certificate, Boolean silent, Oid& oid, ReadOnlyMemory`1& signatureValue)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.CmsSigner.Sign(ReadOnlyMemory`1 data, String contentTypeOid, Boolean silent, X509Certificate2Collection& chainCerts)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] C:\workspace\kleidi\Kleidi\Signing\APK\ApkSigner.cs(176,0): at Kleidi.Signing.APK.ApkSigner.GetRSAData(ZipFile zip, Byte[] sfData, String rsaName, Bundle cert)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] C:\workspace\kleidi\Kleidi\Signing\APK\ApkSigner.cs(58,0): at Kleidi.Signing.APK.ApkSigner.Sign(Stream srcApkStream, Stream& dstApkStream, Bundle certBundle, String sharedKey, String generationId)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] C:\workspace\kleidi\KleidiTests\Signing\APK\ApkSignerTest.cs(23,0): at Kleidi.Signing.APK.ApkSignerTest.Valid_signature_and_certificate_match()
问题是..
有人知道对两者都有效的方法吗?
或者我应该推断操作系统正在构建我然后使用一个或另一个? (听起来很丑)
SignedCms class 似乎无法打开通过 CopyWithPrivateKey 操纵的密钥。对于在这种情况下发生的任何奇怪的事情,快速而肮脏的解决方法是进行临时 PFX 导出并重新导入它,因此替换
X509Certificate2 certWithKey = RSACertificateExtensions.CopyWithPrivateKey(certificate, rsaPriv);
类似
using (X509Certificate2 temp = certificate.CopyWithPrivateKey(rsaPriv))
using (X509Certificate2 certWithKey = new X509Certificate2(temp.Export(X509ContentType.Pfx))
{
// Build CmsSigner and call ComputeSignature here.
}
FWIW:现在这是一个 CoreFX 错误:https://github.com/dotnet/corefx/issues/35120。
我正在尝试找到一种以适用于多平台的方式对 APK SF 文件进行证书签名的方法。目前没有成功;解释我在做什么。
签名(两者通用)
我正在使用发行者和序列号进行签名:
private static byte[] GetSigned(byte[] sfData, X509Certificate cert){
X509Certificate2 certificate = new X509Certificate2(cert.GetEncoded());
RSA rsaPriv = Certificate.ToRSA(cert.KeyPair.Private as RsaPrivateCrtKeyParameters);
X509Certificate2 certWithKey = RSACertificateExtensions.CopyWithPrivateKey(certificate, rsaPriv);
ContentInfo content = new ContentInfo(sfData);
SignedCms signedCms = new SignedCms(content, true);
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certWithKey);
signedCms.ComputeSignature(signer);
return signedCms.Encode();
}
Windows解法
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
{
return CreateRSAProvider(ToRSAParameters(privKey));
}
private static RSA CreateRSAProvider(RSAParameters rp)
{
CspParameters csp = new CspParameters
{
KeyContainerName = string.Format("BouncyCastle-{0}", Guid.NewGuid()),
Flags = CspProviderFlags.UseMachineKeyStore
};
// This is a workaround to fallback to user keystore while not machine is available;
// as otherwise it's impossible having something working on Azure and locally.
// It's more a bug of this cryptography stuff on ASP .Net core..
RSACryptoServiceProvider rsaCsp;
try
{
rsaCsp = new RSACryptoServiceProvider(csp);
}catch(Exception ex)
{
csp.Flags = CspProviderFlags.NoFlags;
rsaCsp = new RSACryptoServiceProvider(csp);
}
rsaCsp.ImportParameters(rp);
return rsaCsp;
}
private static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
RSAParameters rp = new RSAParameters();
rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
rp.P = privKey.P.ToByteArrayUnsigned();
rp.Q = privKey.Q.ToByteArrayUnsigned();
rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);
return rp;
}
Linux解法
在这个
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
{
var rp = ToRSAParameters(privKey);
return RSA.Create(rp);
}
在 Windows 上使用 Linux 解决方案时,出现以下异常
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] System.ArgumentException : The CNG key handle being opened was detected to be ephemeral, but the EphemeralKey open option was not specified.
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] Parameter name: keyHandleOpenOptions
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] Stack Trace:
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.CngKey.Open(SafeNCryptKeyHandle keyHandle, CngKeyHandleOpenOptions keyHandleOpenOptions)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKey[T](X509Certificate2 certificate, Boolean silent, Boolean preferNCrypt)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKeyForSigning[T](X509Certificate2 certificate, Boolean silent)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.CmsSignature.RSAPkcs1CmsSignature.Sign(ReadOnlySpan`1 dataHash, HashAlgorithmName hashAlgorithmName, X509Certificate2 certificate, Boolean silent, Oid& signatureAlgorithm, Byte[]& signatureValue)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.CmsSignature.Sign(ReadOnlySpan`1 dataHash, HashAlgorithmName hashAlgorithmName, X509Certificate2 certificate, Boolean silent, Oid& oid, ReadOnlyMemory`1& signatureValue)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.CmsSigner.Sign(ReadOnlyMemory`1 data, String contentTypeOid, Boolean silent, X509Certificate2Collection& chainCerts)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] at System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] C:\workspace\kleidi\Kleidi\Signing\APK\ApkSigner.cs(176,0): at Kleidi.Signing.APK.ApkSigner.GetRSAData(ZipFile zip, Byte[] sfData, String rsaName, Bundle cert)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] C:\workspace\kleidi\Kleidi\Signing\APK\ApkSigner.cs(58,0): at Kleidi.Signing.APK.ApkSigner.Sign(Stream srcApkStream, Stream& dstApkStream, Bundle certBundle, String sharedKey, String generationId)
[31/01/2019 16:26:09 Informational] [xUnit.net 00:00:03.81] C:\workspace\kleidi\KleidiTests\Signing\APK\ApkSignerTest.cs(23,0): at Kleidi.Signing.APK.ApkSignerTest.Valid_signature_and_certificate_match()
问题是..
有人知道对两者都有效的方法吗? 或者我应该推断操作系统正在构建我然后使用一个或另一个? (听起来很丑)
SignedCms class 似乎无法打开通过 CopyWithPrivateKey 操纵的密钥。对于在这种情况下发生的任何奇怪的事情,快速而肮脏的解决方法是进行临时 PFX 导出并重新导入它,因此替换
X509Certificate2 certWithKey = RSACertificateExtensions.CopyWithPrivateKey(certificate, rsaPriv);
类似
using (X509Certificate2 temp = certificate.CopyWithPrivateKey(rsaPriv))
using (X509Certificate2 certWithKey = new X509Certificate2(temp.Export(X509ContentType.Pfx))
{
// Build CmsSigner and call ComputeSignature here.
}
FWIW:现在这是一个 CoreFX 错误:https://github.com/dotnet/corefx/issues/35120。