public static extern uint NCryptVerifySignature(IntPtr hKey, [In] ref BCRYPT_PSS_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, uint dwFlags);
public static extern uint NCryptSignHash(IntPtr hKey, [In] ref BCRYPT_PSS_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, IntPtr pbSignature, int cbSignature, [Out] out uint pcbResult, uint dwFlags);
public static bool ValidateData(byte[] nonce, byte[] token, byte[] signature, byte[] certificate) { // Convert the Certificate Chain which is in a serialized format to SignedCms object. SignedCms cms = new SignedCms(); cms.Decode(certificate); // Looping through all certificates to find the leaf certificate. X509Certificate2 leafCertificate = null; foreach (X509Certificate2 x509 in cms.Certificates) { bool basicConstraintExtensionExists = false; foreach (X509Extension extension in x509.Extensions) { if (extension.Oid.FriendlyName == "Basic Constraints") { basicConstraintExtensionExists = true; X509BasicConstraintsExtension ext = (X509BasicConstraintsExtension)extension; if (!ext.CertificateAuthority) { leafCertificate = x509; break; } } } if (leafCertificate != null) { break; } if (!basicConstraintExtensionExists) { if (x509.Issuer != x509.Subject) { leafCertificate = x509; break; } } } if (leafCertificate == null) { throw new ArgumentException("Leaf certificate could not be found"); } // Validating the certificate chain. Ignore the errors due to online revocation check not // being available. Also we are not failing validation due to expired certificates. Microsoft // will be revoking the certificates that were exploided. X509Chain chain = new X509Chain(); chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid | X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown | X509VerificationFlags.IgnoreEndRevocationUnknown | X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; chain.ChainPolicy.ApplicationPolicy.Add(new Oid("1.3.6.1.4.1.311.10.5.40")); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; bool result = chain.Build(leafCertificate); if (!result) { foreach (X509ChainStatus status in chain.ChainStatus) { switch (status.Status) { case X509ChainStatusFlags.NoError: case X509ChainStatusFlags.NotTimeValid: case X509ChainStatusFlags.NotTimeNested: case X509ChainStatusFlags.CtlNotTimeValid: case X509ChainStatusFlags.RevocationStatusUnknown: case X509ChainStatusFlags.OfflineRevocation: break; #if DEBUG case X509ChainStatusFlags.UntrustedRoot: break; #endif default: throw new ArgumentException("Chain verification failed with status " + status.Status); } } } // gRootPublicKey is the hard coded public key for the root certificate. // Compare the public key on the root certificate with the hard coded one. // They must match. X509Certificate2 rootCertificate = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; if (!rootCertificate.PublicKey.EncodedKeyValue.RawData.SequenceEqual(gRootPublicKey)) { throw new ArgumentException("Public key of the root certificate is not as expected."); } // Signature contains both nonce and hardwareId. So creating the combined data; byte[] blob; if (nonce == null) { blob = token; } else { blob = nonce.Concat(token).ToArray(); } // Using the leaf Certificate we verify the signature of blob. The RSACryptoServiceProvider does not // provide a way to pass in different padding mode. So we use Win32 NCryptVerifySignature API instead. RSACryptoServiceProvider rsaCsp = leafCertificate.PublicKey.Key as RSACryptoServiceProvider; RSAParameters parameters = rsaCsp.ExportParameters(false); SHA1Managed sha1 = new SHA1Managed(); byte[] blobHash = sha1.ComputeHash(blob); CngKey cngKey = CngKey.Import(GetPublicKey(parameters), CngKeyBlobFormat.GenericPublicBlob); BCRYPT_PSS_PADDING_INFO paddingInfo = new BCRYPT_PSS_PADDING_INFO { pszAlgId = CngAlgorithm.Sha1.Algorithm, cbSalt = 0 }; string[] lines = { "sha1: " + BitConverter.ToInt64(sha1.Hash, 0), " \n blobHash: " + BitConverter.ToInt64(blobHash, 0), "\n blob: " + BitConverter.ToInt64(blob, 0), "\n MachineId:" + BitConverter.ToInt64(token, 0) }; System.IO.File.WriteAllLines(@"C:\Users\YanBorowski\Desktop\Windows8.HardwareToken-master\W8\HardwareTokenSample\HardwareTokenValidationService\log.txt", lines); int result2 = UnsafeNativeMethods.NCryptVerifySignature( cngKey.Handle, ref paddingInfo, blobHash, blobHash.Length, signature, signature.Length, 8); // NCRYPT_PAD_PSS_FLAG if (result2 != 0) // 0 means ERROR_SUCCESS { return false; //throw new ArgumentException("Verification failed with " + result2); } return true; }