/// <summary> /// Creates an RSA PKCS#1 v1.5 or PSS signature of a hash algorithm for the stream. /// </summary> private static byte[] Rsa_Sign( ArraySegment <byte> dataToSign, X509Certificate2 signingCertificate, HashAlgorithmName algorithm, RSASignaturePadding padding) { RSA rsa = null; try { // extract the private key. rsa = signingCertificate.GetRSAPrivateKey(); if (rsa == null) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "No private key for certificate."); } // create the signature. return(rsa.SignData(dataToSign.Array, dataToSign.Offset, dataToSign.Count, algorithm, padding)); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Get private key parameters from a X509Cerificate2. /// The private key must be exportable. /// </summary> private static RsaPrivateCrtKeyParameters GetPrivateKeyParameter(X509Certificate2 certificate) { RSA rsa = null; try { // try to get signing/private key from certificate passed in rsa = certificate.GetRSAPrivateKey(); RSAParameters rsaParams = rsa.ExportParameters(true); RsaPrivateCrtKeyParameters keyParams = new RsaPrivateCrtKeyParameters( new BigInteger(1, rsaParams.Modulus), new BigInteger(1, rsaParams.Exponent), new BigInteger(1, rsaParams.D), new BigInteger(1, rsaParams.P), new BigInteger(1, rsaParams.Q), new BigInteger(1, rsaParams.DP), new BigInteger(1, rsaParams.DQ), new BigInteger(1, rsaParams.InverseQ)); return(keyParams); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Encrypts the message using RSA PKCS#1 v1.5 encryption. /// </summary> private ArraySegment <byte> Rsa_Encrypt( ArraySegment <byte> dataToEncrypt, ArraySegment <byte> headerToCopy, X509Certificate2 encryptingCertificate, bool useOaep) { RSA rsa = null; try { // get the encrypting key. rsa = encryptingCertificate.GetRSAPublicKey(); if (rsa == null) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "No public key for certificate."); } int inputBlockSize = RsaUtils.GetPlainTextBlockSize(rsa, useOaep); int outputBlockSize = RsaUtils.GetCipherTextBlockSize(rsa, useOaep); // verify the input data is the correct block size. if (dataToEncrypt.Count % inputBlockSize != 0) { Utils.Trace("Message is not an integral multiple of the block size. Length = {0}, BlockSize = {1}.", dataToEncrypt.Count, inputBlockSize); } byte[] encryptedBuffer = BufferManager.TakeBuffer(SendBufferSize, "Rsa_Encrypt"); Array.Copy(headerToCopy.Array, headerToCopy.Offset, encryptedBuffer, 0, headerToCopy.Count); using (MemoryStream ostrm = new MemoryStream( encryptedBuffer, headerToCopy.Count, encryptedBuffer.Length - headerToCopy.Count)) { // encrypt body. byte[] input = new byte[inputBlockSize]; for (int ii = dataToEncrypt.Offset; ii < dataToEncrypt.Offset + dataToEncrypt.Count; ii += inputBlockSize) { Array.Copy(dataToEncrypt.Array, ii, input, 0, input.Length); if (useOaep == true) { byte[] cipherText = rsa.Encrypt(input, RSAEncryptionPadding.OaepSHA1); ostrm.Write(cipherText, 0, cipherText.Length); } else { byte[] cipherText = rsa.Encrypt(input, RSAEncryptionPadding.Pkcs1); ostrm.Write(cipherText, 0, cipherText.Length); } } } // return buffer return(new ArraySegment <byte>(encryptedBuffer, 0, (dataToEncrypt.Count / inputBlockSize) * outputBlockSize + headerToCopy.Count)); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Get public key parameters from a X509Certificate2 /// </summary> internal static RsaKeyParameters GetPublicKeyParameter(X509Certificate2 certificate) { RSA rsa = null; try { rsa = certificate.GetRSAPublicKey(); return(GetPublicKeyParameter(rsa)); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Get private key parameters from a X509Certificate2. /// The private key must be exportable. /// </summary> internal static RsaPrivateCrtKeyParameters GetPrivateKeyParameter(X509Certificate2 certificate) { RSA rsa = null; try { // try to get signing/private key from certificate passed in rsa = certificate.GetRSAPrivateKey(); return(GetPrivateKeyParameter(rsa)); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Verify RSA key pair of two certificates. /// </summary> public static bool VerifyRSAKeyPair( X509Certificate2 certWithPublicKey, X509Certificate2 certWithPrivateKey, bool throwOnError = false) { bool result = false; RSA rsaPrivateKey = null; RSA rsaPublicKey = null; try { // verify the public and private key match rsaPrivateKey = certWithPrivateKey.GetRSAPrivateKey(); rsaPublicKey = certWithPublicKey.GetRSAPublicKey(); X509KeyUsageFlags keyUsage = GetKeyUsage(certWithPublicKey); if ((keyUsage & X509KeyUsageFlags.DataEncipherment) != 0) { result = VerifyRSAKeyPairCrypt(rsaPublicKey, rsaPrivateKey); } else if ((keyUsage & X509KeyUsageFlags.DigitalSignature) != 0) { result = VerifyRSAKeyPairSign(rsaPublicKey, rsaPrivateKey); } else { throw new CryptographicException("Don't know how to verify the public/private key pair."); } } catch (Exception e) { if (throwOnError) { throw e; } } finally { RsaUtils.RSADispose(rsaPrivateKey); RsaUtils.RSADispose(rsaPublicKey); if (!result && throwOnError) { throw new CryptographicException("The public/private key pair in the certficates do not match."); } } return(result); }
/// <summary> /// Get public key parameters from a X509Certificate2 /// </summary> private static RsaKeyParameters GetPublicKeyParameter(X509Certificate2 certificate) { RSA rsa = null; try { rsa = certificate.GetRSAPublicKey(); RSAParameters rsaParams = rsa.ExportParameters(false); return(new RsaKeyParameters( false, new BigInteger(1, rsaParams.Modulus), new BigInteger(1, rsaParams.Exponent))); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Verifies an RSA PKCS#1 v1.5 or PSS signature of a hash algorithm for the stream. /// </summary> private static bool Rsa_Verify( ArraySegment <byte> dataToVerify, byte[] signature, X509Certificate2 signingCertificate, HashAlgorithmName algorithm, RSASignaturePadding padding) { RSA rsa = null; try { // extract the public key. rsa = signingCertificate.GetRSAPublicKey(); if (rsa == null) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "No public key for certificate."); } // verify signature. if (!rsa.VerifyData(dataToVerify.Array, dataToVerify.Offset, dataToVerify.Count, signature, algorithm, padding)) { string messageType = new UTF8Encoding().GetString(dataToVerify.Array, dataToVerify.Offset, 4); int messageLength = BitConverter.ToInt32(dataToVerify.Array, dataToVerify.Offset + 4); string actualSignature = Utils.ToHexString(signature); Utils.Trace( "Could not validate signature.\r\nCertificate={0}, MessageType={1}, Length={2}\r\nActualSignature={3}", signingCertificate.Subject, messageType, messageLength, actualSignature); return(false); } return(true); } finally { RsaUtils.RSADispose(rsa); } }
/// <summary> /// Validates the cert and extracts the token from the cert. /// </summary> private static string CheckForToken(X509Certificate2 cert, string name) { if ((cert.SubjectName.Decode(X500DistinguishedNameFlags.None | X500DistinguishedNameFlags.DoNotUseQuotes).Equals("CN=" + name, StringComparison.OrdinalIgnoreCase)) && (DateTime.Now < cert.NotAfter)) { RSA rsa = cert.GetRSAPrivateKey(); if (rsa != null) { foreach (System.Security.Cryptography.X509Certificates.X509Extension extension in cert.Extensions) { // check for instruction code extension if ((extension.Oid.Value == "2.5.29.23") && (extension.RawData.Length >= 4)) { byte[] bytes = new byte[extension.RawData.Length - 4]; Array.Copy(extension.RawData, 4, bytes, 0, bytes.Length); byte[] token = rsa.Decrypt(bytes, RSAEncryptionPadding.OaepSHA1); return(Encoding.ASCII.GetString(token)); } } RsaUtils.RSADispose(rsa); } } return(null); }
/// <summary> /// Creates a cert with the connectionstring (token) and stores it in the given cert store. /// </summary> public async static Task WriteAsync(string name, string connectionString, string storeType, string storePath) { if (string.IsNullOrEmpty(connectionString)) { throw new ArgumentException("Token not found in X509Store and no new token provided!"); } SecureRandom random = new SecureRandom(); KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, 2048); RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator(); keyPairGenerator.Init(keyGenerationParameters); AsymmetricCipherKeyPair keys = keyPairGenerator.GenerateKeyPair(); ArrayList nameOids = new ArrayList(); nameOids.Add(X509Name.CN); ArrayList nameValues = new ArrayList(); nameValues.Add(name); X509Name subjectDN = new X509Name(nameOids, nameValues); X509Name issuerDN = subjectDN; X509V3CertificateGenerator cg = new X509V3CertificateGenerator(); cg.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random)); cg.SetIssuerDN(issuerDN); cg.SetSubjectDN(subjectDN); cg.SetNotBefore(DateTime.Now); cg.SetNotAfter(DateTime.Now.AddMonths(12)); cg.SetPublicKey(keys.Public); cg.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.DataEncipherment)); // encrypt the token with the public key so only the owner of the assoc. private key can decrypt it and // "hide" it in the instruction code cert extension RSA rsa = RSA.Create(); RSAParameters rsaParams = new RSAParameters(); RsaKeyParameters keyParams = (RsaKeyParameters)keys.Public; rsaParams.Modulus = new byte[keyParams.Modulus.ToByteArrayUnsigned().Length]; keyParams.Modulus.ToByteArrayUnsigned().CopyTo(rsaParams.Modulus, 0); rsaParams.Exponent = new byte[keyParams.Exponent.ToByteArrayUnsigned().Length]; keyParams.Exponent.ToByteArrayUnsigned().CopyTo(rsaParams.Exponent, 0); rsa.ImportParameters(rsaParams); if (rsa != null) { byte[] bytes = rsa.Encrypt(Encoding.ASCII.GetBytes(connectionString), RSAEncryptionPadding.OaepSHA1); if (bytes != null) { cg.AddExtension(X509Extensions.InstructionCode, false, bytes); } else { RsaUtils.RSADispose(rsa); throw new CryptographicException("Can not encrypt IoTHub security token using generated public key!"); } } RsaUtils.RSADispose(rsa); // sign the cert with the private key ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", keys.Private, random); Org.BouncyCastle.X509.X509Certificate x509 = cg.Generate(signatureFactory); // create a PKCS12 store for the cert and its private key X509Certificate2 certificate = null; using (MemoryStream pfxData = new MemoryStream()) { Pkcs12StoreBuilder builder = new Pkcs12StoreBuilder(); builder.SetUseDerEncoding(true); Pkcs12Store pkcsStore = builder.Build(); X509CertificateEntry[] chain = new X509CertificateEntry[1]; string passcode = Guid.NewGuid().ToString(); chain[0] = new X509CertificateEntry(x509); pkcsStore.SetKeyEntry(name, new AsymmetricKeyEntry(keys.Private), chain); pkcsStore.Save(pfxData, passcode.ToCharArray(), random); // create X509Certificate2 object from PKCS12 file certificate = CertificateFactory.CreateCertificateFromPKCS12(pfxData.ToArray(), passcode); // handle each store type differently switch (storeType) { case CertificateStoreType.Directory: { // Add to DirectoryStore using (DirectoryCertificateStore store = new DirectoryCertificateStore()) { store.Open(storePath); X509CertificateCollection certificates = await store.Enumerate(); // remove any existing cert with our name from the store foreach (X509Certificate2 cert in certificates) { if (cert.SubjectName.Decode(X500DistinguishedNameFlags.None | X500DistinguishedNameFlags.DoNotUseQuotes).Equals("CN=" + name, StringComparison.OrdinalIgnoreCase)) { await store.Delete(cert.Thumbprint); } } // add new one await store.Add(certificate); } break; } case CertificateStoreType.X509Store: { // Add to X509Store using (X509Store store = new X509Store(storePath, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadWrite); // remove any existing cert with our name from the store foreach (X509Certificate2 cert in store.Certificates) { if (cert.SubjectName.Decode(X500DistinguishedNameFlags.None | X500DistinguishedNameFlags.DoNotUseQuotes).Equals("CN=" + name, StringComparison.OrdinalIgnoreCase)) { store.Remove(cert); } } // add new cert to store try { store.Add(certificate); } catch (Exception e) { throw new Exception($"Not able to add cert to the requested store type '{storeType}' (exception message: '{e.Message}'."); } } break; } default: { throw new Exception($"The requested store type '{storeType}' is not supported. Please change."); } } return; } }