RSA и SignedCms на ASP .Net Core;нужен мультиплатформенный подход - PullRequest
0 голосов
/ 01 февраля 2019

Я пытаюсь найти способ сделать подпись сертификата файла 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

После этого ответа stackoverflow я использовал RSA;как:

public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
        {
            var rp = ToRSAParameters(privKey);
            return RSA.Create(rp);
        }

При использовании решения Linux для Windows я получил следующее исключение

[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()

Вопрос: ..

Кто-нибудь знает способ, который работает для обоих?или я должен сделать вывод, что оперативная система строит меня, а затем использовать один или другой?(звучит некрасиво)

1 Ответ

0 голосов
/ 06 февраля 2019

Похоже, что у класса SignedCms возникли проблемы с открытием ключа, которым манипулировали с помощью 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.

...