using System.Text; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.X509; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.X509.Extension; using X509Certificate = Org.BouncyCastle.X509.X509Certificate; namespace Sophia.Net.DRM { public static class SophiaNetDrmApi { public static X509Certificate CreateSelfSignedCertificate(string commonName, AsymmetricCipherKeyPair keyPair, string sophiaNetExtension = null, bool addSophiaNetPurpose = false) { CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); SecureRandom random = new SecureRandom(randomGenerator); //When acting as a certificate authority, use a proper serial number here - as an argument to this function! BigInteger serial = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random); X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator(); certificateGenerator.SetSerialNumber(serial); X509Name subjectDn = new X509Name(String.Format("CN={0}", commonName)); X509Name issuerDn = subjectDn; certificateGenerator.SetSubjectDN(subjectDn); certificateGenerator.SetIssuerDN(issuerDn); certificateGenerator.SetPublicKey(keyPair.Public); DateTime notBefore = DateTime.Now.Date; DateTime notAfter = notBefore.AddYears(100); certificateGenerator.SetNotBefore(notBefore); certificateGenerator.SetNotAfter(notAfter); certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(false)); AsymmetricCipherKeyPair subjectKeyPair = keyPair; AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair; SubjectKeyIdentifier subjectKeyIdentifier = X509ExtensionUtilities.CreateSubjectKeyIdentifier(keyPair.Public); certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifier); AuthorityKeyIdentifier authorityKeyIdentifier = X509ExtensionUtilities.CreateAuthorityKeyIdentifier(issuerKeyPair.Public); certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifier); if (!string.IsNullOrEmpty(sophiaNetExtension)) { DerObjectIdentifier derObjectIdentifier = new DerObjectIdentifier("1.3.6.1.3.545127833961986897372532.1"); certificateGenerator.AddExtension(derObjectIdentifier, false, System.Text.Encoding.UTF8.GetBytes("sophia.net self-signed certificate")); } List usageList = new List(); usageList.Add(KeyPurposeID.id_kp_serverAuth); usageList.Add(KeyPurposeID.id_kp_codeSigning); if (addSophiaNetPurpose) { DerObjectIdentifier doi = new DerObjectIdentifier("1.3.6.1.3.545127833961986897372532.2"); usageList.Add(doi); } certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(usageList.ToArray())); const string signatureAlgorithm = "SHA256WithRSA"; Asn1SignatureFactory asn1SignatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random); X509Certificate x509Certificate = certificateGenerator.Generate(asn1SignatureFactory); return x509Certificate; } public static AsymmetricCipherKeyPair GenerateKeyPair(int strength = 9216, SecureRandom random = null) { if (random == null) { CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); random = new SecureRandom(randomGenerator); } KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, strength); RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); rsaKeyPairGenerator.Init(keyGenerationParameters); return rsaKeyPairGenerator.GenerateKeyPair(); } public static byte[] ExportPrivateKey(AsymmetricCipherKeyPair keypair) { MemoryStream memoryStream = new MemoryStream(); StreamWriter streamWriter = new StreamWriter(memoryStream); PemWriter pemWriter = new PemWriter(streamWriter); pemWriter.WriteObject(keypair.Private); streamWriter.Flush(); memoryStream.Flush(); return memoryStream.ToArray(); } public static byte[] ExportPublicKey(AsymmetricCipherKeyPair keypair) { MemoryStream memoryStream = new MemoryStream(); StreamWriter streamWriter = new StreamWriter(memoryStream); PemWriter pemWriter = new PemWriter(streamWriter); pemWriter.WriteObject(keypair.Public); streamWriter.Flush(); memoryStream.Flush(); return memoryStream.ToArray(); } public static AsymmetricCipherKeyPair ImportKeyPairFromPrivateKey(byte[] privateBytes) { MemoryStream memoryStream = new MemoryStream(privateBytes); StreamReader streamReader = new StreamReader(memoryStream); PemReader pemReader = new PemReader(streamReader); object privateKey = pemReader.ReadObject(); AsymmetricCipherKeyPair result = privateKey as AsymmetricCipherKeyPair; return result; } public static byte[] ExportCertificate(X509Certificate certificate) { return certificate.GetEncoded(); } public static byte[] ExportCertificateAsPkcs12(AsymmetricKeyParameter privateKey, string password, params X509Certificate[] certificate) { CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); SecureRandom random = new SecureRandom(randomGenerator); Pkcs12StoreBuilder builder = new Pkcs12StoreBuilder(); Pkcs12Store store = builder.Build(); string[] friendlyNames = new string[certificate.Length]; X509CertificateEntry[] certificateEntries = new X509CertificateEntry[certificate.Length]; for (int i = 0; i < certificate.Length; i++) { friendlyNames[i] = certificate[i].SubjectDN.ToString(); certificateEntries[i] = new X509CertificateEntry(certificate[i]); } store.SetCertificateEntry(friendlyNames[0], certificateEntries[0]); store.SetKeyEntry(friendlyNames[0], new AsymmetricKeyEntry(privateKey), certificateEntries); MemoryStream ms = new MemoryStream(); store.Save(ms, password.ToCharArray(), random); return ms.ToArray(); } public static X509Certificate CreateRootCertificate(string commonName, AsymmetricCipherKeyPair keyPair) { CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); SecureRandom random = new SecureRandom(randomGenerator); //When issuing child certificates, use a proper serial number here - as an argument to this function! BigInteger serial = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random); X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator(); certificateGenerator.SetSerialNumber(serial); X509Name subjectDn = new X509Name(String.Format("CN={0}", commonName)); X509Name issuerDn = subjectDn; certificateGenerator.SetSubjectDN(subjectDn); certificateGenerator.SetIssuerDN(issuerDn); certificateGenerator.SetPublicKey(keyPair.Public); DateTime notBefore = DateTime.Now.Date; DateTime notAfter = notBefore.AddYears(100); certificateGenerator.SetNotBefore(notBefore); certificateGenerator.SetNotAfter(notAfter); //BasicConstraints basicConstraints = new BasicConstraints(true); //certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true)); certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(2)); AsymmetricCipherKeyPair subjectKeyPair = keyPair; AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair; SubjectKeyIdentifier subjectKeyIdentifier = X509ExtensionUtilities.CreateSubjectKeyIdentifier(keyPair.Public); certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifier); AuthorityKeyIdentifier authorityKeyIdentifier = X509ExtensionUtilities.CreateAuthorityKeyIdentifier(issuerKeyPair.Public); certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifier); const string signatureAlgorithm = "SHA256WithRSA"; Asn1SignatureFactory asn1SignatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random); X509Certificate x509Certificate = certificateGenerator.Generate(asn1SignatureFactory); return x509Certificate; } public static X509Certificate CreateChildCertificate(string commonName, X509Certificate issuerCert, AsymmetricCipherKeyPair issuerPrivateKey, AsymmetricKeyParameter childPublicKey, long serialNumber, string sophiaNetExtensionValue) { return CreateChildCertificate(commonName, issuerCert, issuerPrivateKey, childPublicKey, serialNumber, Encoding.UTF8.GetBytes(sophiaNetExtensionValue)); } public static X509Certificate CreateChildCertificate(string commonName, X509Certificate issuerCert, AsymmetricCipherKeyPair issuerPrivateKey, AsymmetricKeyParameter childPublicKey, long serialNumber, SophiaNetEntitlementCollection entitlements) { byte[] serialize = entitlements.Serialize(); return CreateChildCertificate(commonName, issuerCert, issuerPrivateKey, childPublicKey, serialNumber, serialize); } public static X509Certificate CreateChildCertificate(string commonName, X509Certificate issuerCert, AsymmetricCipherKeyPair issuerPrivateKey, AsymmetricKeyParameter childPublicKey, long serialNumber, byte[] sophiaNetExtensionValue) { CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); SecureRandom random = new SecureRandom(randomGenerator); BigInteger serial = BigInteger.ValueOf(serialNumber); X509Name subjectDn = new X509Name(String.Format("CN={0}", commonName)); DateTime notBefore = DateTime.Now.Date; DateTime notAfter = issuerCert.NotAfter; DerObjectIdentifier sophiaNetExtensionId = new DerObjectIdentifier("1.3.6.1.3.545127833961986897372532.1"); X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator(); certificateGenerator.SetSerialNumber(serial); certificateGenerator.SetSubjectDN(subjectDn); certificateGenerator.SetIssuerDN(issuerCert.SubjectDN); certificateGenerator.SetPublicKey(childPublicKey); certificateGenerator.SetNotBefore(notBefore); certificateGenerator.SetNotAfter(notAfter); certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(false)); certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(KeyPurposeID.id_kp_clientAuth)); certificateGenerator.AddExtension(sophiaNetExtensionId, false, sophiaNetExtensionValue); const string signatureAlgorithm = "SHA256WithRSA"; Asn1SignatureFactory asn1SignatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerPrivateKey.Private, random); X509Certificate x509Certificate = certificateGenerator.Generate(asn1SignatureFactory); return x509Certificate; } public static X509Certificate ImportCertificate(byte[] encoded) { X509Certificate result = new X509Certificate(encoded); return result; } public static bool ValidateCertificateChain(X509Certificate child, X509Certificate issuer) { try { child.Verify(issuer.GetPublicKey()); return true; } catch (Exception e) { return false; } } public static X509Crl CreateCertificateRevocationList(X509Certificate issuer, AsymmetricCipherKeyPair issuerPrivateKey, params long[] revokedSerials) { CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); SecureRandom random = new SecureRandom(randomGenerator); long notAfterSeconds = issuer.NotAfter.Ticks / TimeSpan.TicksPerSecond; long nowSeconds = DateTime.Now.Ticks / TimeSpan.TicksPerSecond; long secondsDifference = notAfterSeconds - nowSeconds; if (secondsDifference > Int32.MaxValue) secondsDifference = Int32.MaxValue; long _30days = new TimeSpan(30, 0, 0, 0).Ticks / TimeSpan.TicksPerSecond; int max = (int)secondsDifference; int min = (int)_30days; int updateDelayInt = random.Next(min, max); TimeSpan updateDelayTimespan = TimeSpan.FromSeconds(updateDelayInt); X509V2CrlGenerator crlGenerator = new X509V2CrlGenerator(); crlGenerator.SetIssuerDN(issuer.IssuerDN); crlGenerator.SetThisUpdate(DateTime.Now); crlGenerator.SetNextUpdate(DateTime.Now + updateDelayTimespan); crlGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, X509ExtensionUtilities.CreateAuthorityKeyIdentifier(issuer)); crlGenerator.AddExtension(X509Extensions.CrlNumber, false, new CrlNumber(BigInteger.One)); foreach (long revokedSerial in revokedSerials) { crlGenerator.AddCrlEntry(BigInteger.ValueOf(revokedSerial),DateTime.Now, CrlReason.PrivilegeWithdrawn); } const string signatureAlgorithm = "SHA256WithRSA"; Asn1SignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerPrivateKey.Private, random); X509Crl crl = crlGenerator.Generate(signatureFactory); return crl; } public static byte[] ExportCrl(X509Crl crl) { return crl.GetEncoded(); } public static X509Crl ImportCrl(byte[] buffer) { X509Crl result = new X509Crl(buffer); return result; } public static bool ValidateCertificateAgainstCrl(X509Certificate certificate, X509Crl crl) { BigInteger mySerial = certificate.SerialNumber; ISet x509CrlEntries = crl.GetRevokedCertificates(); foreach (X509CrlEntry entry in x509CrlEntries) { BigInteger entrySerial = entry.SerialNumber; if (entrySerial.Equals(mySerial)) return false; } return true; } public static byte[] GetSophiaNetExtensionFromCertificate(X509Certificate cert) { DerObjectIdentifier sophiaNetExtensionId = new DerObjectIdentifier("1.3.6.1.3.545127833961986897372532.1"); X509Extension x509Extension = cert.GetExtension(sophiaNetExtensionId); Asn1OctetString octetString = x509Extension.Value; Stream octetStream = octetString.Parser.GetOctetStream(); Asn1InputStream asn1Reader = new Asn1InputStream(octetStream); DerOctetString readObject = (DerOctetString)asn1Reader.ReadObject(); byte[] result = readObject.GetOctets(); return result; } public static byte[] SignFile(byte[] toBeSigned, AsymmetricKeyParameter privateKey) { RsaDigestSigner signer = new RsaDigestSigner(new Sha256Digest()); signer.Init(true, privateKey); signer.BlockUpdate(toBeSigned, 0, toBeSigned.Length); return signer.GenerateSignature(); } public static bool ValidateSignedFile(byte[] signed, byte[] signature, AsymmetricKeyParameter publicKey) { RsaDigestSigner signer = new RsaDigestSigner(new Sha256Digest()); signer.Init(false, publicKey); signer.BlockUpdate(signed, 0, signed.Length); bool isValid = signer.VerifySignature(signature); return isValid; } } }