ASN.1
Abstract Syntax Notation One is a data definition language that describes the data format for representing, encoding, transmitting and decoding data. The management information database (MIB), application data structure, and protocol data unit (PDU) in the network management system are all defined using ASN.1.
It can be understood that ASN.1 is a specification for the definition of key structure.
Key Structure Type
PKCS#1
RSAPublicKey ::= SEQUENCE { modulus INTEGER, -- n publicExponent INTEGER -- e } RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL }
PKCS#8
PublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, PublicKey BIT STRING ; Among themBIT STRINGIt is a binary format specified by an algorithm itself ; RSAAlgorithm words,It's the aboveRSAPublicKey } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } PrivateKeyInfo ::= SEQUENCE { version Version, algorithm AlgorithmIdentifier, PrivateKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }
Key encoding type
der format
Binary format
Pem format
After encoding the der format data with base64, then add a "----" mark to the beginning and end
Certificate Type
X.509 Certificate
X.509 only contains public keys and does not have private keys. This kind of certificate is generally published publicly and can be used on the client service side for encryption and signature verification.
PKCS#12 Certificate
Because the X.509 certificate only contains the public key, sometimes we need to merge the private key and the public key into a certificate and put it on the server for decryption and signature.
PKCS#12 defines such a certificate that contains both the public key and the private key. A typical pfx and p12 certificate is a PKCS#12 certificate.
PKCS#7 Certificate
When you receive a website's certificate, you need to verify its authenticity. Because an X.509 certificate contains the public key, holder information, and signature. To verify its authenticity, you need to visa its signature, and verifying the signature requires the public key certificate of the CA institution issued. By the same principle, after you get the public key certificate of the CA institution, you also need to verify the authenticity of the CA institution. To verify the certificate of the CA institution, you need the CA public key certificate of the CA superior institution... and so on, you need to verify until the root certificate. So in order to verify the authenticity of a website certificate, you need not only a certificate, but a certificate chain. PKCS#7 defines the type structure of such a certificate chain. Typical certificates with p7b suffix names are in this format.
Certificate suffix
.cer/.crt: Stores the public key, without a private key, it is an X.509 certificate, stored in binary form
.pfx/.p12: Stores public and private keys, usually including protection passwords, binary
Certificate and key relationship
The digital certificate and the private key are matching relationships. It's like the relationship between a key card and a key. When issuing a digital certificate, the digital certificate issuance system (CA system) will randomly generate a pair of keys, a private key, and a public key while generating a digital certificate. The digital certificate indicates the user's identity. The matching private and public keys are used to ensure the authenticity of the user's identity. It’s like we hold a bunch of keys, each key indicates the keys of a certain room sometimes, but whether it is true or not depends on whether the corresponding door can be opened.
Key generation
/// <summary> /// Get the private key and public key XML format, return the array first as the private key and the second as the public key. /// </summary> /// <param name="size">key length, default 1024, can be 2048</param> /// <returns></returns> public static string[] CreateXmlKey(int size = 1024) { //The key format is to generate pkcs#1 format instead of pkcs#8 format RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size); string privateKey = (true);//private key string publicKey = (false);//public key return new string[] { privateKey, publicKey }; } /// <summary> /// Get the private key and public key in CspBlob format, return the first one of the array is the private key and the second one is the public key. /// </summary> /// <param name="size"></param> /// <returns></returns> public static string[] CreateCspBlobKey(int size = 1024) { //The key format is to generate pkcs#1 format instead of pkcs#8 format RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size); string privateKey = .ToBase64String((true));//private key string publicKey = .ToBase64String((false));//public key return new string[] { privateKey, publicKey }; } /// <summary> /// Export PEM PKCS#1 format key pair, return the array first is the private key and the second is the public key. /// </summary> public static string[] CreateKey_PEM_PKCS1(int size = 1024) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size); string privateKey = RSA_PEM.ToPEM(rsa, false, false); string publicKey = RSA_PEM.ToPEM(rsa, true, false); return new string[] { privateKey, publicKey }; } /// <summary> /// Export PEM PKCS#8 format key pair, the first one is the private key and the second one is the public key. /// </summary> public static string[] CreateKey_PEM_PKCS8(int size = 1024, bool convertToPublic = false) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size); string privateKey = RSA_PEM.ToPEM(rsa, false, true); string publicKey = RSA_PEM.ToPEM(rsa, true, true); return new string[] { privateKey, publicKey }; }
Use backend append/decryption method
/// <summary> /// RSA encryption /// </summary> /// <param name="Data">Original text</param> /// <param name="PublicKeyString">Public key</param> /// <param name="KeyType">KeyType XML/PEM</param> /// <returns></returns> public static string RSAEncrypt(string Data,string PublicKeyString,string KeyType) { byte[] data = ("UTF-8").GetBytes(Data); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); switch (KeyType) { case "XML": (PublicKeyString); break; case "PEM": rsa = RSA_PEM.FromPEM(PublicKeyString); break; default: throw new Exception("Unsupported key type"); } //The maximum length limit of the encrypted block. If the length of the encrypted data exceeds the key length/8-11, an incorrect length will be caused, so the block encryption of the data will be performed. int MaxBlockSize = / 8 - 11; //Normal length if ( <= MaxBlockSize) { byte[] hashvalueEcy = (data, false); //encryption return .ToBase64String(hashvalueEcy); } //The length exceeds the normal value else { using (MemoryStream PlaiStream = new MemoryStream(data)) using (MemoryStream CrypStream = new MemoryStream()) { Byte[] Buffer = new Byte[MaxBlockSize]; int BlockSize = (Buffer, 0, MaxBlockSize); while (BlockSize > 0) { Byte[] ToEncrypt = new Byte[BlockSize]; (Buffer, 0, ToEncrypt, 0, BlockSize); Byte[] Cryptograph = (ToEncrypt, false); (Cryptograph, 0, ); BlockSize = (Buffer, 0, MaxBlockSize); } return .ToBase64String((), ); } } } /// <summary> /// RSA decryption /// </summary> /// <param name="Data">ciphertext</param> /// <param name="PrivateKeyString">Private Key</param> /// <param name="KeyType">KeyType XML/PEM</param> /// <returns></returns> public static string RSADecrypt(string Data,string PrivateKeyString, string KeyType) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); switch (KeyType) { case "XML": (PrivateKeyString); break; case "PEM": rsa = RSA_PEM.FromPEM(PrivateKeyString); break; default: throw new Exception("Unsupported key type"); } int MaxBlockSize = / 8; //Maximum length limit for decryption block //Decryption normally if ( <= MaxBlockSize) { byte[] hashvalueDcy = (.FromBase64String(Data), false);//Decryption return ("UTF-8").GetString(hashvalueDcy); } //Segmented decryption else { using (MemoryStream CrypStream = new MemoryStream(.FromBase64String(Data))) using (MemoryStream PlaiStream = new MemoryStream()) { Byte[] Buffer = new Byte[MaxBlockSize]; int BlockSize = (Buffer, 0, MaxBlockSize); while (BlockSize > 0) { Byte[] ToDecrypt = new Byte[BlockSize]; (Buffer, 0, ToDecrypt, 0, BlockSize); Byte[] Plaintext = (ToDecrypt, false); (Plaintext, 0, ); BlockSize = (Buffer, 0, MaxBlockSize); } string output = ("UTF-8").GetString(()); return output; } } }
Front-end encryption method
Note: jsencrypt default PKCS#1 structure, you need to pay attention to when generating keys
<script src="/scripts/"></script> var encryptor = new JSEncrypt() // Create an encrypted object instance //Be careful not to have spaces when copying the public key generated by SSL var pubKey = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1QQRl0HlrVv6kGqhgonD6A9SU6ZJpnEN+Q0blT/ue6Ndt97WRfxtS'+ 'As0QoquTreaDtfC4RRX4o+CU6BTuHLUm+eSvxZS9TzbwoYZq7ObbQAZAY+SYDgAA5PHf1wNN20dGMFFgVS/y0ZWvv1UNa2laEz0I8Vmr5ZlzIn88GkmSiQIDAQAB-----END PUBLIC KEY-----' (pubKey)//Set the public key var rsaPassWord = ('The content to be encrypted') // Encrypt content
c#pem format conversion
Note: The RSACryptoServiceProvider of C# only supports xml format key resolution by default
public class RSA_Unit { static public string Base64EncodeBytes(byte[] byts) { return .ToBase64String(byts); } static public byte[] Base64DecodeBytes(string str) { try { return .FromBase64String(str); } catch { return null; } } /// <summary> /// break the string by how many words per line /// </summary> static public string TextBreak(string text, int line) { var idx = 0; var len = ; var str = new StringBuilder(); while (idx < len) { if (idx > 0) { ('\n'); } if (idx + line >= len) { ((idx)); } else { ((idx, line)); } idx += line; } return (); } } static public class Extensions { /// <summary> /// Copy a copy from the array start to the specified length /// </summary> static public T[] sub<T>(this T[] arr, int start, int count) { T[] val = new T[count]; for (var i = 0; i < count; i++) { val[i] = arr[start + i]; } return val; } static public void writeAll(this Stream stream, byte[] byts) { (byts, 0, ); } } Click and drag to move public class RSA_PEM { public static RSACryptoServiceProvider FromPEM(string pem) { var rsaParams = new CspParameters(); = ; var rsa = new RSACryptoServiceProvider(rsaParams); var param = new RSAParameters(); var base64 = _PEMCode.Replace(pem, ""); var data = RSA_Unit.Base64DecodeBytes(base64); if (data == null) { throw new Exception("PEM content is invalid"); } var idx = 0; //Read length Func<byte, int> readLen = (first) => { if (data[idx] == first) { idx++; if (data[idx] == 0x81) { idx++; return data[idx++]; } else if (data[idx] == 0x82) { idx++; return (((int)data[idx++]) << 8) + data[idx++]; } else if (data[idx] < 0x80) { return data[idx++]; } } throw new Exception("PEM failed to extract data"); }; //Read block data Func<byte[]> readBlock = () => { var len = readLen(0x02); if (data[idx] == 0x00) { idx++; len--; } var val = (idx, len); idx += len; return val; }; //Compare whether data starts from idx position is byts content Func<byte[], bool> eq = (byts) => { for (var i = 0; i < ; i++, idx++) { if (idx >= ) { return false; } if (byts[i] != data[idx]) { return false; } } return true; }; if (("PUBLIC KEY")) { /***Use public key****/ //Total length of reading data readLen(0x30); if (!eq(_SeqOID)) { throw new Exception("PEM unknown format"); } //Read 1 length readLen(0x03); idx++;//Skip 0x00 //Read 2 lengths readLen(0x30); //Modulus = readBlock(); //Exponent = readBlock(); } else if (("PRIVATE KEY")) { /***Use private key****/ //Total length of reading data readLen(0x30); //Read the version number if (!eq(_Ver)) { throw new Exception("PEM unknown version"); } //Detection of PKCS8 var idx2 = idx; if (eq(_SeqOID)) { //Read 1 length readLen(0x04); //Read 2 lengths readLen(0x30); //Read the version number if (!eq(_Ver)) { throw new Exception("PEM version is invalid"); } } else { idx = idx2; } //Read data = readBlock(); = readBlock(); = readBlock(); = readBlock(); = readBlock(); = readBlock(); = readBlock(); = readBlock(); } else { throw new Exception("pem requires BEGIN END header"); } (param); return rsa; } static private Regex _PEMCode = new Regex(@"--+.+?--+|\s+"); static private byte[] _SeqOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; static private byte[] _Ver = new byte[] { 0x02, 0x01, 0x00 }; /// <summary> /// Convert the key pair in RSA to PEM format, usePKCS8=false to return PKCS#1 format, otherwise return PKCS#8 format. If convertToPublic RSA with private key will only return the public key, and RSA with public key only will not be affected. /// </summary> public static string ToPEM(RSACryptoServiceProvider rsa, bool convertToPublic, bool usePKCS8) { ///p/25803dd9527d ///ylz8401/p/ ///jiayanhui2877/article/details/47187077 ///xuanshao_/article/details/51679824 ///xuanshao_/article/details/51672547 var ms = new MemoryStream(); //Write a length byte code Action<int> writeLenByte = (len) => { if (len < 0x80) { ((byte)len); } else if (len <= 0xff) { (0x81); ((byte)len); } else { (0x82); ((byte)(len >> 8 & 0xff)); ((byte)(len & 0xff)); } }; //Write a piece of data Action<byte[]> writeBlock = (byts) => { var addZero = (byts[0] >> 4) >= 0x8; (0x02); var len = + (addZero ? 1 : 0); writeLenByte(len); if (addZero) { (0x00); } (byts, 0, ); }; //Write length data according to the length of subsequent content Func<int, byte[], byte[]> writeLen = (index, byts) => { var len = - index; (0); (byts, 0, index); writeLenByte(len); (byts, index, len); return (); }; if ( || convertToPublic) { /***Generate public key****/ var param = (false); //Write the total number of bytes, excluding the length of this section, an additional 24 bytes is required, and the subsequent calculation is completed and filled in (0x30); var index1 = (int); //Fixed content // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" (_SeqOID); //The subsequent length starting from 0x00 (0x03); var index2 = (int); (0x00); //Length of subsequent content (0x30); var index3 = (int); //Write to Modulus writeBlock(); //Write to Exponent writeBlock(); //Calculate the length of the vacancy var byts = (); byts = writeLen(index3, byts); byts = writeLen(index2, byts); byts = writeLen(index1, byts); return "-----BEGIN PUBLIC KEY-----\n" + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "\n-----END PUBLIC KEY-----"; } else { /***Generate private key****/ var param = (true); //Write the total number of bytes, and subsequent writes (0x30); int index1 = (int); //Write the version number (_Ver); //PKCS8 More data int index2 = -1, index3 = -1; if (usePKCS8) { //Fixed content (_SeqOID); //Length of subsequent content (0x04); index2 = (int); //Length of subsequent content (0x30); index3 = (int); //Write the version number (_Ver); } //Write data writeBlock(); writeBlock(); writeBlock(); writeBlock(); writeBlock(); writeBlock(); writeBlock(); writeBlock(); //Calculate the length of the vacancy var byts = (); if (index2 != -1) { byts = writeLen(index3, byts); byts = writeLen(index2, byts); } byts = writeLen(index1, byts); var flag = " PRIVATE KEY"; if (!usePKCS8) { flag = " RSA" + flag; } return "-----BEGIN" + flag + "-----\n" + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "\n-----END" + flag + "-----"; } } }
The above is a detailed explanation of the interoperability of RSA encryption between C# and JS. For more information about the interoperability of RSA encryption between C# and JS, please follow my other related articles!