Skip to content

Instantly share code, notes, and snippets.

@uzzu
Last active August 27, 2018 00:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save uzzu/e3807becdc3729a8dd4b to your computer and use it in GitHub Desktop.
Save uzzu/e3807becdc3729a8dd4b to your computer and use it in GitHub Desktop.
RSA公開鍵(.pem)をxml stringに変換するEditorScript
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