비밀 키를 문자열로 또는 그 반대로 변환


102

키를 생성 중이며 DB에 저장해야하므로 문자열로 변환하지만 문자열에서 키를 다시 가져옵니다. 이를 수행하는 가능한 방법은 무엇입니까?

내 코드는

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

문자열에서 키를 어떻게 되 찾을 수 있습니까?


1
키를 문자열로 변환하는 것은 절대적으로 필요한 경우에만 수행해야합니다. String키 객체와 바이트 배열을 지울 수있는 동안 Java에서 인스턴스 를 삭제하는 명시적인 방법은 없습니다 . 이는 키가 더 오랜 기간 동안 메모리 내에서 사용 가능한 상태를 유지할 수 있음을 의미합니다. (비밀번호 보호) 사용 KeyStore, 가급적 런타임 시스템 / OS 또는 하드웨어에 의해 지원되는 것이 선호되어야합니다.
— Maarten Bodewes

답변:


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 Codec , Bouncy 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"클래스의 패키지는 무엇입니까
— Swap L

@SwapL 그것은 android.util.Base64입니다. 이 링크를 확인 developer.android.com/reference/android/util/Base64.html
— 자바리

@ MaartenBodewes-owlstead 대부분의 사람들은 아직 Java 8을 사용하지 않습니다. 나는 이것을 안드로이드에서 사용했는데, 그것은 확실히 아직 8에 있지 않다 (그리고 아마 당분간은 아닐 것이다). 맥락을 가정하여 다른 사람의 답변을 편집하지 마십시오.
— Jabari

@ MaartenBodewes-owlstead 귀하의 의견은 내 첫 번째 문장을 완전히 무시합니다. "대부분의 사람들은 아직 Java 8을 사용하지 않습니다." 귀하의 답변은 대다수의 Java 사용자, Android 및 비 Android 모두에게 예외 오류를 발생시킵니다. 즉, 현재 답변에 스 니펫을 추가하는 것이 더 완벽한 솔루션을 제공 할 것입니다. 참고로, 나는 내 대답과 관련하여 "감정적"이 아닙니다. 사실, 보안 측면에서 확실히 개선 되었기 때문에 DES를 AES로 바꿨습니다 (원래 질문의 코드와 더 일치 함).
— Jabari

@ MaartenBodewes-owlstead 다시 ... 추가 한 내용은 "NoSuchAlgorithmException"예외 오류를 발생시킵니다. 참조하십시오 : docs.oracle.com/javase/7/docs/api/javax/crypto/… 수정하겠습니다 ...
— Jabari

5

빠르게 실패 하는 일부 함수를 만드는 것이 얼마나 재미 있는지 보여주기 위해 다음 세 가지 함수를 작성했습니다.

하나는 AES 키를 만들고 하나는이를 인코딩하고 다른 하나는 다시 디코딩합니다. 이 세 가지 메소드는 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

1

실제로 Luis가 제안한 것은 저에게 효과가 없었습니다. 나는 다른 방법을 찾아야했다. 이것이 나를 도왔습니다. 당신도 도울 수 있습니다. 연결:

  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는 Serializable에서 상속되는 java.security.Key에서 상속됩니다. 따라서 여기서 핵심은 (말장난이 아님) 키를 ByteArrayOutputStream으로 직렬화하고 byte [] 배열을 가져 와서 db에 저장하는 것입니다. 반대 프로세스는 db에서 byte [] 배열을 가져오고, byte [] 배열의 ByteArrayInputStream을 만들고, SecretKey를 deserialize하는 것입니다.

... 또는 더 간단하게 .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

SecretKeySpec을 String으로 또는 그 반대로 변환 :getEncoded() in 값 을 가져 오는 데 사용할 수있는, SecretKeySpec제공 하는 메서드를 byteArray사용할 수 있습니다 .encodeToString()stringSecretKeySpecBase64 객체 .

변환하는 동안 SecretKeySpec에 String: 사용 decode()에하는 Base64줄 것이다 byteArray, 그에서 당신을 위해 인스턴스를 만들 수 있습니다 SecretKeySpec(가)로 PARAMS와 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.