Research on creating an X509 Certificate and obtaining the certificate key
Author: Xiao Bo
Personal blog: http://blog.csdn.net/eaglet; http://www.cnblogs.com/eaglet
2007/7 Nanjing
Background
The format of the server SSL digital certificate and the client unit digital certificate complies with the X.509 standard. X.509 is a digital certificate standard developed by the ITU (ITU-T. In order to provide public network user directory information services, ITU set the X.500 series standards in 1988. X.500 And X.509 are the core of the Security Authentication System. X.500 defines a different naming rule to ensure the uniqueness of user names using the naming tree; x.509 provides a communication entity identification mechanism for X.500 user names and specifies a certificate syntax and data interface widely used in Entity authentication. X.509 is called a certificate.
The identification framework provided by X.509 is a public key-based authentication service key management system. A user has two keys: one is the user's private key (for short, the Private Key), and the other is the Public Key (for short, the Public Key) that other users can obtain and use ). Users can use conventional encryption algorithms (such as DES) to encrypt information, and then use the receiver's public key to encrypt the DES and attach it to the information, in this way, the receiver can use the corresponding private key to open the DES password lock and decrypt the information. This authentication framework allows users to store their public keys in the CA directory. If a user wants to exchange secret information with another user, he can directly obtain the corresponding public key from the other user's directory for various security services.
The original X.509 version was published on July 15, 1988, and the recommendation draft of version 3 was published on July 15, 1994, and was approved on July 15, 1995. Essentially, an X.509 Certificate consists of a user's public key and User Identifier. It also includes the version number, certificate serial number, CA identifier, signature algorithm identifier, issuer name, and certificate validity period. You can provide the CA with its public key to obtain the certificate in a secure and reliable manner, so that the user can publish the certificate, and any public key that requires the user can obtain the certificate, check whether the key is correct through CA. The latest version of this standard, X.509 version 3, provides an extended field for digital certificates containing extended information to provide more flexibility and transfer of required information in special environments.
To perform identity authentication, the X.509 standard and public key encryption system provide a solution called digital signature. You can generate a piece of information and its summary (also known as "fingerprint "). The user uses a dedicated key to encrypt the abstract to form a signature. The receiver decrypts the signature with the public key of the sender and compares it with the received message "fingerprint" to determine its authenticity.
Currently, the X.509 standard has been widely accepted in the orchestration of Public Key formats and has been used in many network security applications, including IP Security (Ipsec) and Secure Sockets Layer (SSL), Secure Electronic Transaction (SET), secure multimedia INTERNET Mail Extension (S/MIME), etc.
CreateX509Certificate
There are many ways to create an X509 Certificate. In Windows, the following methods are summarized,
1) obtain the certificate through CA,
2) obtain the test certificate through the makecert tool provided by Microsoft
3) programming method creation.. Net provides the X509Certificate2 class, which can be used to create a certificate, but can only be created from RawData. After creation, no attributes other than FriendlyName can be modified.
I have been searching for a long time on the Internet and have never found a method to create a custom certificate through a program. Later I thought of a compromise, that is, using a program to call Mr. makecert.exe to form a certificate. Some parameters of the certificate, such as Subject, validity period, and serial number, can be passed in through parameters, then read the generated Certificate file to Rawdata to obtain the X509Certificate2 type certificate object. Of course, this method is really stupid and must depend on external processes. If I have time later, I still want to follow the X509 V3 standard to create RawData and then generate a certificate. This should be a more flexible approach. Do you have any better methods to create a custom certificate.
The code for creating an X509 Certificate through makecert.exe is as follows:
Static object semObj = new object ();
/// <Summary>
/// Custom Certificate Information
/// </Summary>
Public class T_CertInfo
{
Public String FriendlyName;
Public String Subject;
Public DateTime BeginDate;
Public DateTime EndDate;
Public int SerialNumber;
}
/// <Summary>
/// Generate an X509 Certificate
/// </Summary>
/// <Param name = "makecrtPath"> directory of the makecert Process </param>
/// <Param name = "crtPath"> temporary directory of the Certificate file </param>
/// <Param name = "certInfo"> Certificate Information </param>
/// <Returns> </returns>
Public static X509Certificate2 CreateCertificate (String makecrtPath, String crtPath,
T_CertInfo certInfo)
{
Debug. Assert (certInfo! = Null );
Debug. Assert (certInfo. Subject! = Null );
String MakeCert = makecrtPath + "makecert.exe ";
String fileName = crtPath + "cer ";
String userName = Guid. NewGuid (). ToString ();
StringBuilder arguments = new StringBuilder ();
Arguments. AppendFormat ("-r-n \" {0} \ "-ss my-sr currentuser-sky exchange ",
CertInfo. Subject );
If (certInfo. SerialNumber> 0)
{
Arguments. AppendFormat ("-# {0}", certInfo. SerialNumber );
}
Arguments. AppendFormat ("-B {0}", certInfo. BeginDate. ToString (@ "MM \/dd \/yyyy "));
Arguments. AppendFormat ("-e {0}", certInfo. EndDate. ToString (@ "MM \/dd \/yyyy "));
Arguments. AppendFormat ("\" {0} \ "", fileName );
Lock (semObj)
{
Process p = Process. Start (MakeCert, arguments. ToString ());
P. WaitForExit ();
Byte [] certBytes = ReadFile (fileName );
X509Certificate2 cert = new X509Certificate2 (certBytes );
Cert = new X509Certificate2 (certBytes );
If (certInfo. FriendlyName! = Null)
{
Cert. FriendlyName = certInfo. FriendlyName;
}
Return cert;
}
}
Internal static byte [] ReadFile (string fileName)
{
Using (FileStream f = new FileStream (fileName, FileMode. Open, FileAccess. Read ))
{
Int size = (int) f. Length;
Byte [] data = new byte [size];
Size = f. Read (data, 0, size );
Return data;
}
}
Obtain the certificate Private Key
The X509 Certificate obtained through the above method can only obtain its public key information, because the public key and private key appear in pairs, if we want to use this certificate in the program for encryption and decryption, you must obtain the private key corresponding to the public key. In the same way, there is no good solution on the Internet. You can only study it on your own. We have summarized two methods to share with you:
Method 1:
Obtain the private key from the key container. The specific method is as follows:
First, add a-sk keyname to the makecert parameter to specify the location of the key container of the topic, which contains the private key. If the key container does not exist, the system creates one.
Then, after executing the p. WaitForExit (); statement, run the following statement to obtain the private key and private key parameters.
RSAParameters privateKey;
RSACryptoServiceProvider rsa = GetKeyFromContainer ("keyname ");
PrivateKey = rsa. ExportParameters (true );
Public static RSACryptoServiceProvider GetKeyFromContainer (string ContainerName)
{
// Create the CspParameters object and set the key container
// Name used to store the RSA key pair.
CspParameters cp = new CspParameters ();
Cp. KeyContainerName = ContainerName;
// Create a new instance of RSACryptoServiceProvider that accesses
// The key container MyKeyContainerName.
Return new RSACryptoServiceProvider (cp );
}
One disadvantage of this method is that the caller of the program must have the permission to read the key container. For a Web application, because the IIS Guest account does not have this permission, the key in the key container cannot be read. Trying to simulate Super User Logon (NetworkSecurity. ImpersonateUser) cannot solve this problem, and I personally think it is not very good for website security. Later, I came up with the second method: simply reset the key pair and replace the key pair in the certificate with the key pair generated by myself. I tried it and it worked well.
Method 2:
To reset a key pair, follow these steps:
First, you must generate an encryption algorithm and a key with the same number of digits as the certificate key generated by makecert. The test shows that when makecert uses an exchange key, a 1024-bit RSA key is generated by default, and the Exponent is, 0, 1.
RSACryptoServiceProvider has the same default key. Therefore, you only need to use RSACryptoServiceProvider RSA = new RSACryptoServiceProvider () to generate a key.
The second step is to replace the public key parameter in the key file with the public key parameter to be replaced.
RSAParameters publicKey;
RSAParameters privateKey;
RSACryptoServiceProvider RSA = (RSACryptoServiceProvider) cert. PublicKey. Key;
PublicKey = RSA. ExportParameters (false );
// Locate the position of the public key parameter in RawData
If (publicKey. Modulus. Length! = 128 | publicKey. Exponent. Length! = 3)
{
Throw new Exception ("public key module lenght! = 128! ");
}
If (publicKey. Exponent [0]! = 1 |
PublicKey. Exponent [1]! = 0 |
PublicKey. Exponent [2]! = 1)
{
Throw new Exception ("public key Exponent! = 101! ");
}
Byte [] module = publicKey. Modulus;
Int I = 0;
Int matchCnt = 0;
Int modulePos = 0;
Int j = 0;
While (I <certBytes. Length)
{
If (certBytes [I] = module [j]) // if cerBytes is RawData, which of the following code can be used to create a certificate?
{
I ++;
J ++;
MatchCnt ++;
If (matchCnt = 128)
{
ModulePos = I-128;
Break;
}
}
Else
{
If (matchCnt = 128)
{
ModulePos = I-128;
Break;
}
Else
{
MatchCnt = 0;
J = 0;
I ++;
}
}
}
// Create a key pair
RSA = new RSACryptoServiceProvider ();
PublicKey = RSA. ExportParameters (false );
PrivateKey = RSA. ExportParameters (true );
// The public key parameter in the key pair to be reset overwrites the original Parameter
J = 0;
For (I = modulePos; I <modulePos + 128; I ++)
{
CertBytes [I] = publicKey. Modulus [j];
J ++;
}
// Re-create the certificate with the New Parameter
Cert = new X509Certificate2 (certBytes );
In this way, privateKey becomes the private key of the new certificate.
Problems with this method:
The problem with this method is that the public key information in the certificate is searched by matching. This is a lazy method. The correct method should be based on the standard definition, since there is not much time to study the standards, I am a little lazy, but I feel that this method is still effective for the moment. Please try again later.