3

私はたくさんの質問を見てきましたが、どれも本当に良い答えを提供していません。あるいは、彼らが試みた場合、彼らは質問をしている人が実際に何を求めているのかを尋ねていないと思い込んでいます。念のため、署名、暗号化、PEMファイルについては質問していません。

私の場合、暗号化されたデータの小さなファイルを受け取ります。RSAキーペアの秘密キーを使用して暗号化されています。

私が利用できる公開鍵はASN.1形式のDERファイルであり、それ自体は証明書ではなく、署名もされていません。

Javaでは、これは簡単です。C#では、私が言えることからすると悪夢です。X509Certificate2は、キーのバイト配列を解析しようとすると、「オブジェクトが見つかりません」と要求します。

キーは、次のopenSSLコマンドで生成されました。

>openssl genrsa -out private_key.pem 2048

Generating RSA private key, 2048 bit long modulus
...........+++
....................................................................................................
...........................+++
e is 65537 (0x10001)

>openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt

>openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
writing RSA key

編集:私が試しているコード:

private byte[] ReadBytesFromStream(string streamName)
{
    using (Stream stream = this.GetType().Assembly.GetManifestResourceStream(streamName))
    {
        byte[] result = new byte[stream.Length];
        stream.Read(result, 0, (int) stream.Length);
        return result;
    }
}

public void LoadPublicKey()
{
    byte[] key = ReadBytesFromStream("my.namespace.Resources.public_key.der");
    X509Certificate2 myCertificate;
    try
    {
        myCertificate = new X509Certificate2(key);
    }
    catch (Exception e)
    {
        throw new CryptographicException(String.Format("Unable to open key file. {0}", e));
    } 
}

ReadBytesFromStreamメソッドは正しいバイトストリームを返します。public_key.derファイルは埋め込みリソースです。

4

1 に答える 1

1

これを磨くだけです。以下のコードはまだ少し乱雑ですが、うまくいくようです。

Windows で公開キーを使用してメッセージを復号化できない理由については、 http ://www.derkeiler.com/Newsgroups/microsoft.public.dotnet.security/2004-05/0270.html で説明しています。

次に、C# コードのどこかに公開鍵モジュラスと指数が必要です。それがファイルにあるか、埋め込まれているかは、ここでは問題ではありません。ただし、Base64 を使用してバイナリ キーをラップします。Windows が実際にインポートするキーを生成することをあきらめました。さらに、私が必要としているのは、公開鍵が埋め込みリソースになることです。

私が抱えていた最後の問題は、キーから生成されたモジュラスが Windows (c#) で機能しないことでした。このStackOverflowの投稿は、それを解決するための鍵を握っていました.Windowsのモジュラスには先行ゼロを含めることはできません. Android アプリ内課金 Dot Net(C#)でのレシート確認

Java から生成された係数は、c# では機能しませんでした。その投稿のstripLeadingZerosコードを使用して、有効な公開鍵モジュラスを作成します。

  private static byte[] stripLeadingZeros(byte[] a) {
    int lastZero = -1;
    for (int i = 0; i < a.length; i++) {
      if (a[i] == 0) {
        lastZero = i;
      }
      else {
        break;
      }
    }
    lastZero++;
    byte[] result = new byte[a.length - lastZero];
    System.arraycopy(a, lastZero, result, 0, result.length);
    return result;
  }

手始めに、これがキーファイルの生成方法です。

$ openssl genrsa -out private_key.pem 2048

$ openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt

$ openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der

Java コード:

package com.example.signing;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import jcifs.util.Base64;

public class SigningExample {
    private static String PRIVATE_KEY_FILE = "private_key.der";
    private static String PUBLIC_KEY_FILE  = "public_key.der";

    /**
     * @param args
     */
    public static void main(String[] args) {
        SigningExample.sign();
    }

    private static void sign() {
        try {
            String text = new String(SigningExample.loadFromFile("message.xml"));
            String message = Base64.encode(text.getBytes());

            RSAPrivateKey privateKey = PrivateRSAKeyReader.getKeyFile(SigningExample.PRIVATE_KEY_FILE);
            RSAPublicKey publicKey = PublicRSAKeyReader.getKeyFile(SigningExample.PUBLIC_KEY_FILE);

            byte[] modulusBytes = publicKey.getModulus().toByteArray();
            byte[] exponentBytes = publicKey.getPublicExponent().toByteArray();

            modulusBytes = SigningExample.stripLeadingZeros(modulusBytes);

            String modulusB64 = Base64.encode(modulusBytes);
            String exponentB64 = Base64.encode(exponentBytes);

            System.out.println("Copy these two into your c# code:");
            System.out.println("DotNet Modulus : " + modulusB64);
            System.out.println("DotNet Exponent: " + exponentB64);

            // Testing
            byte[] signature = SigningExample.sign(message.getBytes(), privateKey, "SHA1withRSA");

            String signedMessage = message + "\n" + Base64.encode(signature);

            SigningExample.saveToBase64File("message.signed", signedMessage.getBytes());

            System.out.println("\nMessage  :\n" + signedMessage);

            String[] newkey = new String(SigningExample.loadFromBase64File("message.signed")).split("\\n");
            System.out.println("Verified : " + SigningExample.verify(newkey[0].getBytes(), publicKey, "SHA1withRSA", Base64.decode(newkey[1])));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static byte[] stripLeadingZeros(byte[] a) {
        int lastZero = -1;
        for (int i = 0; i < a.length; i++) {
            if (a[i] == 0) {
                lastZero = i;
            } else {
                break;
            }
        }
        lastZero++;
        byte[] result = new byte[a.length - lastZero];
        System.arraycopy(a, lastZero, result, 0, result.length);
        return result;
    }

    private static byte[] sign(byte[] data, PrivateKey prvKey,
            String sigAlg) throws Exception {
        Signature sig = Signature.getInstance(sigAlg);
        sig.initSign(prvKey);
        sig.update(data, 0, data.length);
        return sig.sign();
    }

    private static boolean verify(byte[] data, PublicKey pubKey,
            String sigAlg, byte[] sigbytes) throws Exception {
        Signature sig = Signature.getInstance(sigAlg);
        sig.initVerify(pubKey);
        sig.update(data, 0, data.length);
        return sig.verify(sigbytes);
    }

    public static void saveToBase64File(String fileName, byte[] data) throws IOException {
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));

        try {
            String b64 = Base64.encode(data);
            int lineLength = 64;
            int idx = 0;
            int len = b64.length();

            while (len - idx >= lineLength) {
                out.write(b64.substring(idx, idx + lineLength).getBytes());
                out.write('\n');
                idx += lineLength;
            }
            out.write(b64.substring(idx, len).getBytes());
        } catch (Exception e) {
            throw new IOException("Unexpected error", e);
        } finally {
            out.close();
        }
    }

    public static byte[] loadFromBase64File(String fileName) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(fileName));

        try {
            StringBuffer sb = new StringBuffer();

            String buffer;

            while ((buffer = br.readLine()) != null) {
                sb.append(buffer);
            }

            return Base64.decode(sb.toString());
        } catch (Exception e) {
            throw new IOException("Unexpected error", e);
        } finally {
            br.close();
        }
    }

    public static void saveToFile(String fileName, byte[] data) throws IOException {
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));

        try {
            out.write(data);
        } catch (Exception e) {
            throw new IOException("Unexpected error", e);
        } finally {
            out.close();
        }
    }

    public static byte[] loadFromFile(String fileName) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(fileName));

        try {
            StringBuffer sb = new StringBuffer();

            String buffer;

            while ((buffer = br.readLine()) != null) {
                sb.append(buffer);
            }

            return sb.toString().getBytes();
        } catch (Exception e) {
            throw new IOException("Unexpected error", e);
        } finally {
            br.close();
        }
    }
}

class PrivateRSAKeyReader {

    public static RSAPrivateKey getKeyFile(String filename) throws Exception {

        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        byte[] keyBytes = new byte[(int) f.length()];
        dis.readFully(keyBytes);
        dis.close();

        return PrivateRSAKeyReader.generateKey(keyBytes);
    }

    public static RSAPrivateKey getKey(String base64) throws Exception {
        byte[] keyBytes = Base64.decode(base64);

        return PrivateRSAKeyReader.generateKey(keyBytes);
    }

    private static RSAPrivateKey generateKey(byte[] keyBytes) throws Exception {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(spec);
    }
}

class PublicRSAKeyReader {

    public static RSAPublicKey getKeyFile(String filename) throws Exception {

        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        byte[] keyBytes = new byte[(int) f.length()];
        dis.readFully(keyBytes);
        dis.close();

        return PublicRSAKeyReader.generateKey(keyBytes);
    }

    public static RSAPublicKey getKey(String base64) throws Exception {
        byte[] keyBytes = Base64.decode(base64);

        return PublicRSAKeyReader.generateKey(keyBytes);
    }

    private static RSAPublicKey generateKey(byte[] keyBytes) throws Exception {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(spec);
    }
}

C# では、次のコード フラグメントでトリックを実行する必要があります。

String signedMessage = ""; // load Base64 coded the message generated in Java here.

byte[] message = Convert.FromBase64String(signedMessage);
String messageString = Encoding.ASCII.GetString(message);

String[] lines = Regex.Split(messageString, "\n");

byte[] content = Convert.FromBase64String(lines[0]); // first line of the message were the content
byte[] signature = Convert.FromBase64String(lines[1]); // second line were the signature

RSACryptoServiceProvider rsaObj = new RSACryptoServiceProvider(2048);

//Create a new instance of the RSAParameters structure.
RSAParameters rsaPars = new RSAParameters();

rsaPars.Modulus = Convert.FromBase64String("insert your modulus revealed in the Java example here");
rsaPars.Exponent = Convert.FromBase64String("AQAB"); // Exponent. Should always be this.

// Import key parameters into RSA.
rsaObj.ImportParameters(rsaPars);

// verify the message
Console.WriteLine(rsaObj.VerifyData(Encoding.ASCII.GetBytes(lines[0]), "SHA1", signature));

コードは次を使用しています:

using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

最終的なメッセージを Base64 の別のレイヤーにパックするのは少しやり過ぎであり、変更する必要があることを率直に認めます。

于 2012-04-25T17:29:19.740 に答える