Last active
August 27, 2018 00:27
-
-
Save uzzu/e3807becdc3729a8dd4b to your computer and use it in GitHub Desktop.
RSA公開鍵(.pem)をxml stringに変換するEditorScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.IO; | |
using System.Text; | |
using System.Security.Cryptography; | |
using UnityEngine; | |
using UnityEditor; | |
public static class RsaKeyDecoder | |
{ | |
#region constants | |
const string ScriptNamePrefix = "RsaKeyDecoder."; | |
const string PemPrefix = "-----BEGIN"; | |
const string PemPublicHeader = "-----BEGIN PUBLIC KEY-----"; | |
const string PemPublicFooter = "-----END PUBLIC KEY-----"; | |
const string PemPrivateHeader = "-----BEGIN RSA PRIVATE KEY-----"; | |
const string PemPrivateFooter = "-----END RSA PRIVATE KEY-----"; | |
const string PemPrivatePkcs8Header = "-----BEGIN PRIVATE KEY-----"; | |
const string PemPrivatePkcs8Footer = "-----END PRIVATE KEY-----"; | |
const string PemPrivatePkcs8EncryptedHeader = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; | |
const string PemPrivatePkcs8EncryptedFooter = "-----END ENCRYPTED PRIVATE KEY-----"; | |
#endregion | |
#region MenuItem | |
[MenuItem("Assets/RSA public key(.pem) to XML")] | |
public static void MenuItemRsaPublicKeyPemToXml() | |
{ | |
var scriptName = "MenuItemRsaPublicKeyPemToXml"; | |
var path = EditorUtility.OpenFilePanel("Read A PublicKey pem file", "", "pem"); | |
if (string.IsNullOrEmpty(path)) | |
{ | |
return; | |
} | |
var result = RsaPublicKeyPemToXml(scriptName, path); | |
if (string.IsNullOrEmpty(result)) | |
{ | |
return; | |
} | |
var dialogResult = EditorUtility.DisplayDialogComplex("RsaPublicKeyToXml", result, "Copy to Clipboard", "Save File", "Close"); | |
if (dialogResult == 0) | |
{ | |
EditorGUIUtility.systemCopyBuffer = result; | |
return; | |
} | |
if (dialogResult == 1) | |
{ | |
var savePath = EditorUtility.SaveFilePanel("Save As", "", "rsapkeypem", ".xml"); | |
if (string.IsNullOrEmpty(savePath)) | |
{ | |
return; | |
} | |
File.WriteAllText(savePath, result); | |
return; | |
} | |
} | |
#endregion | |
#region RsaPublicKeyPemToXml | |
static string RsaPublicKeyPemToXml(string scriptName, string pemFilePath) | |
{ | |
try | |
{ | |
var pem = GetSource(pemFilePath); | |
var result = DecodeRsaPublicKeyPemToXml(pem); | |
return result; | |
} | |
catch (Exception e) | |
{ | |
Debug.Log(string.Format("[{0}{1}]Error: {1}, {2}", ScriptNamePrefix, scriptName, e.Message, e.StackTrace)); | |
} | |
return string.Empty; | |
} | |
static string DecodeRsaPublicKeyPemToXml(string pem) | |
{ | |
if (string.IsNullOrEmpty(pem)) | |
{ | |
throw new ArgumentException("File is empty"); | |
} | |
if (!pem.StartsWith(PemPrefix)) | |
{ | |
throw new NotImplementedException("DER key was not supported."); | |
} | |
if (!pem.StartsWith(PemPublicHeader) || !pem.EndsWith(PemPublicFooter)) | |
{ | |
if (pem.StartsWith(PemPrivateHeader) && pem.EndsWith(PemPrivateFooter)) | |
{ | |
throw new NotImplementedException("RSA Private Key was not supported"); | |
} | |
throw new NotImplementedException("Pem PKCS #8 was not supported."); | |
} | |
Debug.Log(string.Format("[{0}] Start Decode RSA Public Key(.pem) to XML", ScriptNamePrefix)); | |
var publicKey = DecodeOpenSslPublicKey(pem); | |
var rsa = DecodeX509PublicKey(publicKey); | |
var result = rsa.ToXmlString(false); | |
return result; | |
} | |
static byte[] DecodeOpenSslPublicKey(string source) | |
{ | |
var builder = new StringBuilder(source); | |
builder.Replace(PemPublicHeader, string.Empty); | |
builder.Replace(PemPublicFooter, string.Empty); | |
var publicStr = builder.ToString().Trim(); | |
var result = Convert.FromBase64String(publicStr); | |
return result; | |
} | |
static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509Key) | |
{ | |
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" | |
var SeqOid = new byte[] { | |
0x30, | |
0x0D, | |
0x06, | |
0x09, | |
0x2A, | |
0x86, | |
0x48, | |
0x86, | |
0xF7, | |
0x0D, | |
0x01, | |
0x01, | |
0x01, | |
0x05, | |
0x00 | |
}; | |
var seq = new byte[15]; | |
// set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob | |
var stream = new MemoryStream(x509Key); | |
using (var reader = new BinaryReader(stream)) | |
{ | |
var b = 0x00; | |
var twobytes = (ushort) 0; | |
twobytes = reader.ReadUInt16(); | |
// data read as little endian order (actual data order for Sequence is 30 81) | |
if (twobytes == 0x8130) | |
{ | |
reader.ReadByte(); | |
} | |
else if (twobytes == 0x8230) | |
{ | |
reader.ReadInt16(); | |
} | |
else | |
{ | |
throw new ArgumentException("Invalid format."); | |
} | |
// read the Sequence OID | |
seq = reader.ReadBytes(15); | |
if (!ByteArrayEquals(seq, SeqOid)) | |
{ | |
throw new ArgumentException("Invalid format encoded OID."); | |
} | |
twobytes = reader.ReadUInt16(); | |
// data read as little endian order (actual data order for Bit String is 03 81) | |
if (twobytes == 0x8103) | |
{ | |
reader.ReadByte(); | |
} | |
else if (twobytes == 0x8203) | |
{ | |
reader.ReadInt16(); | |
} | |
else | |
{ | |
throw new ArgumentException("Invalid format."); | |
} | |
// expect null byte next | |
b = reader.ReadByte(); | |
if (b != 0x00) | |
{ | |
throw new ArgumentException("Invalid format."); | |
} | |
// data read as little endian order (actual data order for Sequence is 30 81) | |
twobytes = reader.ReadUInt16(); | |
if (twobytes == 0x8130) | |
{ | |
reader.ReadByte(); | |
} | |
else if (twobytes == 0x8230) | |
{ | |
reader.ReadInt16(); | |
} | |
else | |
{ | |
throw new ArgumentException("Invalid format."); | |
} | |
twobytes = reader.ReadUInt16(); | |
var lowbyte = (byte) 0x00; | |
var highbyte = (byte) 0x00; | |
// data read as little endian order (actual data order for Integer is 02 81) | |
if (twobytes == 0x8102) | |
{ | |
// read next bytes which is bytes in modulus | |
lowbyte = reader.ReadByte(); | |
} | |
else if (twobytes == 0x8202) | |
{ | |
// advance 2 bytes | |
highbyte = reader.ReadByte(); | |
lowbyte = reader.ReadByte(); | |
} | |
else | |
{ | |
throw new ArgumentException("Invalid format."); | |
} | |
// reverse byte order since asn.1 key uses big endian order | |
var modint = new byte[] { lowbyte, highbyte, 0x00, 0x00 }; | |
var modsize = BitConverter.ToInt32(modint, 0); | |
var firstbyte = reader.ReadByte(); | |
reader.BaseStream.Seek(-1, SeekOrigin.Current); | |
if (firstbyte == 0x00) | |
{ | |
// if first byte (highest order) of modulus is zero, don't include it | |
// skip this null byte | |
reader.ReadByte(); | |
// reduce modulus buffer size by 1 | |
modsize -= 1; | |
} | |
// modulus bytes expect an Integer for the exponent data | |
var modulus = reader.ReadBytes(modsize); | |
if (reader.ReadByte() != 0x02) | |
{ | |
throw new ArgumentException("Invalid format."); | |
} | |
// should only need one byte for actual exponent data (for all useful values) | |
var expbytes = (int) reader.ReadByte(); | |
var exponent = reader.ReadBytes(expbytes); | |
// create RSACryptoServiceProvider instance and initialize with public key | |
var result = new RSACryptoServiceProvider(); | |
var rsaKeyInfo = new RSAParameters(); | |
rsaKeyInfo.Modulus = modulus; | |
rsaKeyInfo.Exponent = exponent; | |
result.ImportParameters(rsaKeyInfo); | |
return result; | |
} | |
} | |
#endregion | |
#region General Private Methods | |
static string GetSource(string pemFilePath) | |
{ | |
pemFilePath = pemFilePath.Trim(); | |
if (string.IsNullOrEmpty(pemFilePath)) | |
{ | |
throw new FileNotFoundException("File path is empty."); | |
} | |
if (!File.Exists(pemFilePath)) | |
{ | |
throw new FileNotFoundException(string.Format("File not found => {0}", pemFilePath)); | |
} | |
var result = string.Empty; | |
using (var reader = File.OpenText(pemFilePath)) | |
{ | |
result = reader.ReadToEnd().Trim(); | |
} | |
return result; | |
} | |
static bool ByteArrayEquals(byte[] a, byte[] b) | |
{ | |
if (a.Length != b.Length) | |
{ | |
return false; | |
} | |
for (var i = 0; i < a.Length; i++) | |
{ | |
if (a[i] != b[i]) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment