0

저는이 주제를 처음 접했고 PEM 형식 대 CER 형식의 공개 키의 차이점을 혼동합니다.x509certificate2 오브젝트에서 pem 형식으로 공개 키를 내 보냅니다.

x509certificate2 개체의 공개 키를 PEM 형식의 C# 코드로 내보내려고합니다.

내가 아는 한, cer 형식 대 pem 형식의 인증서 차이는 머리말과 꼬리말 일뿐입니다. (올바르게 이해하면 base 64의 .cer 형식 인증서는 someBase64String이어야하고 pem이어야합니다 형식은 시작 및 끝 머리글과 바닥 글을 포함하여 동일한 문자열입니다.)

하지만 내 질문은 공개 키입니다. 가 pubKey가 x509certificate2 개체에서 .CER 형식으로 내 보낸 공개 키, 이 키의 PEM 형식입니다하자이 될 것입니다 : 을 ------ BEGIN PUBLIC KEY ----- pubKey ... ------ END PUBLIC KEY ------ 은 64 진수로 인코딩 되었습니까?

감사합니다 :) 공개 키에 대한

답변

1

. pubKey는 x509certificate2 객체에서 .cer 형식으로 내 보낸 공개 키가됩니다.

".cer 형식"에 대한 이야기는 전체 인증서가있는 경우에만 적용됩니다. 그것이 X509Certificate2가 내보낼 모든 것입니다. (음, 또는 인증서 모음 또는 연관된 개인 키가있는 인증서 모음).

.NET에 내장 된 것은 PEM 인코딩에서 "공개 키"가되는 DER로 인코딩 된 SubjectPublicKeyInfo 블록 인증서를 제공하지 않습니다.

원하는 경우 사용자가 직접 데이터를 만들 수 있습니다. RSA는 그리 좋지는 않지만 전체적으로 즐겁습니다. 데이터 포맷은 https://tools.ietf.org/html/rfc3280#section-4.1에 정의되어

SubjectPublicKeyInfo ::= SEQUENCE { 
    algorithm   AlgorithmIdentifier, 
    subjectPublicKey  BIT STRING } 

AlgorithmIdentifier ::= SEQUENCE { 
    algorithm    OBJECT IDENTIFIER, 
    parameters    ANY DEFINED BY algorithm OPTIONAL } 

https://tools.ietf.org/html/rfc3279#section-2.3.1는 RSA 키, 특히 부호화되는 방법을 설명

rsaEncryption OID가 값의 알고리즘 필드 에서 사용할 수있다

AlgorithmIdentifier 유형의 매개 변수 필드는 반드시 이 알고리즘 식별자에 대해 ASN.1 타입의 NULL을 가져야한다.

RSA 공개 키는 ASN.1 유형 RSAPublicKey 사용하여 인코딩해야합니다

RSAPublicKey ::= SEQUENCE { 
    modulus   INTEGER, -- n 
    publicExponent  INTEGER } -- e 

이러한 구조 뒤에 언어는 ASN.1은, ITU X.680에 의해 정의를 길들이는 바이트로 인코딩 된 얻을 ITU X.690의 DER (Distinguished Encoding Rules) 룰 세트가 적용됩니다.

는 .NET 실제로 다시이 조각의 많은 당신을 제공하지만, 당신이 그들을 조립해야합니다 :

private static string BuildPublicKeyPem(X509Certificate2 cert) 
{ 
    byte[] algOid; 

    switch (cert.GetKeyAlgorithm()) 
    { 
     case "1.2.840.113549.1.1.1": 
      algOid = new byte[] { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; 
      break; 
     default: 
      throw new ArgumentOutOfRangeException(nameof(cert), $"Need an OID lookup for {cert.GetKeyAlgorithm()}"); 
    } 

    byte[] algParams = cert.GetKeyAlgorithmParameters(); 
    byte[] publicKey = WrapAsBitString(cert.GetPublicKey()); 

    byte[] algId = BuildSimpleDerSequence(algOid, algParams); 
    byte[] spki = BuildSimpleDerSequence(algId, publicKey); 

    return PemEncode(spki, "PUBLIC KEY"); 
} 

private static string PemEncode(byte[] berData, string pemLabel) 
{ 
    StringBuilder builder = new StringBuilder(); 
    builder.Append("-----BEGIN "); 
    builder.Append(pemLabel); 
    builder.AppendLine("-----"); 
    builder.AppendLine(Convert.ToBase64String(berData, Base64FormattingOptions.InsertLineBreaks)); 
    builder.Append("-----END "); 
    builder.Append(pemLabel); 
    builder.AppendLine("-----"); 

    return builder.ToString(); 
} 

private static byte[] BuildSimpleDerSequence(params byte[][] values) 
{ 
    int totalLength = values.Sum(v => v.Length); 
    byte[] len = EncodeDerLength(totalLength); 
    int offset = 1; 

    byte[] seq = new byte[totalLength + len.Length + 1]; 
    seq[0] = 0x30; 

    Buffer.BlockCopy(len, 0, seq, offset, len.Length); 
    offset += len.Length; 

    foreach (byte[] value in values) 
    { 
     Buffer.BlockCopy(value, 0, seq, offset, value.Length); 
     offset += value.Length; 
    } 

    return seq; 
} 

private static byte[] WrapAsBitString(byte[] value) 
{ 
    byte[] len = EncodeDerLength(value.Length + 1); 
    byte[] bitString = new byte[value.Length + len.Length + 2]; 
    bitString[0] = 0x03; 
    Buffer.BlockCopy(len, 0, bitString, 1, len.Length); 
    bitString[len.Length + 1] = 0x00; 
    Buffer.BlockCopy(value, 0, bitString, len.Length + 2, value.Length); 
    return bitString; 
} 

private static byte[] EncodeDerLength(int length) 
{ 
    if (length <= 0x7F) 
    { 
     return new byte[] { (byte)length }; 
    } 

    if (length <= 0xFF) 
    { 
     return new byte[] { 0x81, (byte)length }; 
    } 

    if (length <= 0xFFFF) 
    { 
     return new byte[] 
     { 
      0x82, 
      (byte)(length >> 8), 
      (byte)length, 
     }; 
    } 

    if (length <= 0xFFFFFF) 
    { 
     return new byte[] 
     { 
      0x83, 
      (byte)(length >> 16), 
      (byte)(length >> 8), 
      (byte)length, 
     }; 
    } 

    return new byte[] 
    { 
     0x84, 
     (byte)(length >> 24), 
     (byte)(length >> 16), 
     (byte)(length >> 8), 
     (byte)length, 
    }; 
} 

DSA 및 ECDSA 키 AlgorithmIdentifier에 대한 더 복잡한 값이 있습니다.매개 변수를 사용하지만 X509Certificate의 GetKeyAlgorithmParameters()는 올바른 형식으로 반환하기 때문에 OID (문자열) 조회 키와 OID (바이트 []) 인코딩 된 값을 switch 문에 써야합니다.

필자의 SEQUENCE 및 BIT STRING 빌더는 확실히 성능이 좋을 수 있지만 (성능이 좋지 않은 모든 어레이를 살펴보십시오.), 이는 성능에 이상이없는 것으로 충분할 것입니다.

결과를 확인하려면 출력을 openssl rsa -pubin -text -noout에 붙여 넣을 수 있으며 오류가 아닌 다른 것을 인쇄하면 RSA 키에 대해 합법적으로 인코딩 된 "공개 키"인코딩을 만들었습니다.

관련 문제