秘密鍵の文字列への変換とその逆


102

キーを生成してDBに格納する必要があるので、それを文字列に変換しますが、文字列からキーを取得します。これを達成するための可能な方法は何ですか?

私のコードは、

SecretKey key = KeyGenerator.getInstance("AES").generateKey();
String stringKey=key.toString();
System.out.println(stringKey);

文字列からキーを取得するにはどうすればよいですか?


1
キーから文字列への変換は、どうしても必要な場合にのみ実行してください。Stringキーオブジェクトとバイト配列がクリアされる可能性がある間、Javaでインスタンスを破棄する明示的な方法はありません。これは、キーがメモリ内で長期間利用できることを意味します。(パスワードで保護された)を使用するKeyStoreことをお勧めします。ランタイムシステム/ OSまたはハードウェアでサポートされているものが望ましいです。
Maarten Bodewes 2017

回答:


272

SecretKeyバイト配列(byte[])に変換してから、Base64でそれをにエンコードできStringます。に変換して戻すにはSecretKey、Base64で文字列をデコードし、それをaで使用しSecretKeySpecて元のファイルを再構築しますSecretKey

Java 8の場合

文字列へのSecretKey:

// create new key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
// get base64 encoded version of the key
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());

SecretKeyへの文字列:

// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); 

Java 7以前(Androidを含む)の場合:

注I: Base64エンコード/デコード部分をスキップしてbyte[]、SQLiteに格納するだけです。そうは言っても、Base64エンコード/デコードの実行はコストのかかる操作ではなく、ほとんどすべてのDBに問題なく文字列を格納できます。

注II:以前のJavaバージョンでは、java.langまたはjava.utilパッケージのいずれかにBase64が含まれていません。ただし、Apache Commons CodecBouncy Castle、またはGuavaのコーデックを使用することは可能です。

文字列へのSecretKey:

// CREATE NEW KEY
// GET ENCODED VERSION OF KEY (THIS CAN BE STORED IN A DB)

    SecretKey secretKey;
    String stringKey;

    try {secretKey = KeyGenerator.getInstance("AES").generateKey();}
    catch (NoSuchAlgorithmException e) {/* LOG YOUR EXCEPTION */}

    if (secretKey != null) {stringKey = Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT)}

SecretKeyへの文字列:

// DECODE YOUR BASE64 STRING
// REBUILD KEY USING SecretKeySpec

    byte[] encodedKey     = Base64.decode(stringKey, Base64.DEFAULT);
    SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");

@Jabari「Base64」クラスのパッケージとは何か
スワップL

@SwapL android.util.Base64です。このリンクを確認してください:developer.android.com/reference/android/util/Base64.html
Jabari

@ MaartenBodewes-owlsteadほとんどの人はまだJava 8を使用していません。私はこれをAndroidで使用しましたが、これは確かにまだ8には含まれていません(おそらくしばらくはそうではありません)。文脈を前提として誰かの回答を編集しないでください。
Jabari 14

@ MaartenBodewes-owlsteadあなたのコメントは私の最初の文を完全に無視しています:「ほとんどの人はまだJava 8を使っていません」。あなたの答えは、Javaの大多数のユーザー(AndroidとAndroid以外)に例外エラーをスローします。つまり、現在の回答に加えてスニペットを追加するという提案は、より完全なソリューションを提供します。ちなみに、私は私の回答に関して「感傷的」ではありません。実際のところ、私はDESをAESに交換しました。これは、セキュリティ面での明確な改善であるため(そして元の質問のコードとより一致しているため)です。
Jabari 14

@ MaartenBodewes-owlsteadもう一度...追加したものは、「NoSuchAlgorithmException」例外エラーをスローします。参照してください:docs.oracle.com/javase/7/docs/api/javax/crypto/… 修正します...
Jabari

5

フェイルファストであるいくつかの関数を作成することがどれほど楽しいかを示すために、次の3つの関数を作成しました。

1つはAESキーを作成し、1つはそれをエンコードし、もう1つはデコードして戻します。これらの3つのメソッドは、Java 8で使用できます(内部クラスまたは外部の依存関係なしで)。

public static SecretKey generateAESKey(int keysize)
        throws InvalidParameterException {
    try {
        if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
            // this may be an issue if unlimited crypto is not installed
            throw new InvalidParameterException("Key size of " + keysize
                    + " not supported in this runtime");
        }

        final KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(keysize);
        return keyGen.generateKey();
    } catch (final NoSuchAlgorithmException e) {
        // AES functionality is a requirement for any Java SE runtime
        throw new IllegalStateException(
                "AES should always be present in a Java SE runtime", e);
    }
}

public static SecretKey decodeBase64ToAESKey(final String encodedKey)
        throws IllegalArgumentException {
    try {
        // throws IllegalArgumentException - if src is not in valid Base64
        // scheme
        final byte[] keyData = Base64.getDecoder().decode(encodedKey);
        final int keysize = keyData.length * Byte.SIZE;

        // this should be checked by a SecretKeyFactory, but that doesn't exist for AES
        switch (keysize) {
        case 128:
        case 192:
        case 256:
            break;
        default:
            throw new IllegalArgumentException("Invalid key size for AES: " + keysize);
        }

        if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
            // this may be an issue if unlimited crypto is not installed
            throw new IllegalArgumentException("Key size of " + keysize
                    + " not supported in this runtime");
        }

        // throws IllegalArgumentException - if key is empty
        final SecretKeySpec aesKey = new SecretKeySpec(keyData, "AES");
        return aesKey;
    } catch (final NoSuchAlgorithmException e) {
        // AES functionality is a requirement for any Java SE runtime
        throw new IllegalStateException(
                "AES should always be present in a Java SE runtime", e);
    }
}

public static String encodeAESKeyToBase64(final SecretKey aesKey)
        throws IllegalArgumentException {
    if (!aesKey.getAlgorithm().equalsIgnoreCase("AES")) {
        throw new IllegalArgumentException("Not an AES key");
    }

    final byte[] keyData = aesKey.getEncoded();
    final String encodedKey = Base64.getEncoder().encodeToString(keyData);
    return encodedKey;
}

2
キーストアがハードウェアセキュリティモジュール(またはgetEncoded()使用できない他の場所)にある場合、キーの格納/取得が機能しない場合があることに注意してください。
Maarten Bodewes 2016年

1

実際、ルイスが提案したことは私にはうまくいきませんでした。私は別の方法を考え出さなければなりませんでした。これは私を助けたものです。あなたも助けるかもしれません。リンク:

  1. * .getEncoded():https ://docs.oracle.com/javase/7/docs/api/java/security/Key.html

  2. エンコーダ情報:https : //docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. デコーダー情報:https : //docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

コードスニペット:エンコードの場合:

String temp = new String(Base64.getEncoder().encode(key.getEncoded()));

デコード:

byte[] encodedKey = Base64.getDecoder().decode(temp);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "DES");

0

使いたくない .toString()

SecretKeyはjava.security.Keyから継承され、それ自体がSerializableから継承されていることに注意してください。したがって、ここでのキー(しゃれた意図はありません)は、キーをByteArrayOutputStreamにシリアル化し、byte []配列を取得してそれをdbに格納することです。逆のプロセスは、db []配列をデータベースから取得し、ByteArrayInputStreamを作成して、byte []配列をオフにし、SecretKeyを非直列化します...

...またはもっと簡単に、単に .getEncoded() java.security.Key(SecretKeyの親インターフェース)から継承されメソッドをます。このメソッドは、Key / SecretKeyからエンコードされたbyte []配列を返します。これは、データベースに格納したり、データベースから取得したりできます。

これはすべて、SecretKey実装がエンコードをサポートしていることを前提としています。さもないと、getEncoded() nullを返します。

編集:

Key / SecretKey javadocs(Googleページの最初にあります)を見てください。

http://download.oracle.com/javase/6/docs/api/java/security/Key.html

またはこれはCodeRanchから(同じGoogle検索でも見つかります):

http://www.coderanch.com/t/429127/java/java/Convertion-between-SecretKey-String-or


Serializableは、別のアプローチをとるたびに、最近のIMOの反パターンです。base64がエンコードおよびデコードする承認済みの回答の方がはるかに優れています。
user2223059

0

StringにSecretKeySpecの変換およびその逆: あなたは使用することができgetEncoded()た方法SecretKeySpec与えるbyteArray、あなたが使用できることから、encodeToString()取得することstringの価値SecretKeySpecではBase64オブジェクトを。

SecretKeySpecへの変換中String:use decode()in Base64will give byteArray、それからSecretKeySpecparamsを使用してをインスタンス化し、byteArrayを再現できますSecretKeySpec

String mAesKey_string;
SecretKeySpec mAesKey= new SecretKeySpec(secretKey.getEncoded(), "AES");

//SecretKeySpec to String 
    byte[] byteaes=mAesKey.getEncoded();
    mAesKey_string=Base64.encodeToString(byteaes,Base64.NO_WRAP);

//String to SecretKeySpec
    byte[] aesByte = Base64.decode(mAesKey_string, Base64.NO_WRAP);
    mAesKey= new SecretKeySpec(aesByte, "AES");

-1

これを試してください、それはBase64なしで動作します(これはJDK 1.8にのみ含まれています)、このコードは以前のJavaバージョンでも実行されます:)

private static String SK = "Secret Key in HEX";


//  To Encrupt

public static String encrypt( String Message ) throws Exception{

    byte[] KeyByte = hexStringToByteArray( SK);
    SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");

    Cipher c = Cipher.getInstance("DES","SunJCE");
    c.init(1, k);
    byte mes_encrypted[] = cipher.doFinal(Message.getBytes());

    String MessageEncrypted = byteArrayToHexString(mes_encrypted);
    return MessageEncrypted;
}

//  To Decrypt

public static String decrypt( String MessageEncrypted )throws Exception{

    byte[] KeyByte = hexStringToByteArray( SK );
    SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");

    Cipher dcr =  Cipher.getInstance("DES","SunJCE");
    dc.init(Cipher.DECRYPT_MODE, k);
    byte[] MesByte  = hexStringToByteArray( MessageEncrypted );
    byte mes_decrypted[] = dcipher.doFinal( MesByte );
    String MessageDecrypeted = new String(mes_decrypted);

    return MessageDecrypeted;
}

public static String byteArrayToHexString(byte bytes[]){

    StringBuffer hexDump = new StringBuffer();
    for(int i = 0; i < bytes.length; i++){
    if(bytes[i] < 0)
    {   
        hexDump.append(getDoubleHexValue(Integer.toHexString(256 - Math.abs(bytes[i]))).toUpperCase());
    }else
    {
        hexDump.append(getDoubleHexValue(Integer.toHexString(bytes[i])).toUpperCase());
    }
    return hexDump.toString();

}



public static byte[] hexStringToByteArray(String s) {

    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2)
    {   
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
    }
    return data;

} 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.