2014-11-20 1 views
0

저는 스프링 MVC 응용 프로그램에서 작업 중이며 암호 재설정 기능을 위해 username + Joda Date가 포함 된 전자 메일을 보내고 있습니다. 그것을 보내기 전에 나는 완벽하게 반대 될 수있는 방식으로 내용 (날짜 + 사용자 이름)을 암호화하고 싶습니다. 나의 이론적 이해가가는 한, 암호화의 목적을 처음부터 뒤엎는 것이 아닌가? 그렇지 않다면, 나는 그들을 암호화하고, 그런 다음 해독 할 몇 가지 메커니즘을 원합니다. 내가 찾은 몇 가지 해결책이 있는데, 그 중 하나는 검색에 대한 언급이지만 엔티티가 어떤 것인지 파악할 수 없습니다. 아래 코드를 붙여 넣습니다.암호화 조타 날짜와 사용자 이름을 단일 변수로 결합했습니다.

암호화 및 암호 해독 : 친절하게 봐 가지고

byte[] userBytes = username.getBytes("UTF-8"); 
byte[] keyBytes = key.getBytes("UTF-8"); 
//XOR scramble 
byte[] encrypted = new byte[userBytes.length]; 
for(int i = 0; i < userBytes.length; i++){ 
    encrypted[i] = (byte)(userBytes[i]^keyBytes[i % keyBytes.length]); 
} 

BASE64Encoder encoder = new BASE64Encoder(); 
String encoded = encoder.encode(encrypted); 

// webappB, decode the parameter 
BASE64Decoder decoder = new BASE64Decoder(); 
byte[] decoded = decoder.decodeBuffer(encoded); 
//XOR descramble 
byte[] decrypted = new byte[decoded.length]; 
for(int i = 0; i < decoded.length; i++){ 
    decrypted[i] = (byte)(decoded[i]^keyBytes[i % keyBytes.length]); 
} 
+0

암호화를 사용할 때 어떤 유형의 공격을 방지해야할까요? 참고 : 보내는 모든 링크의 키를 변경하지 않으면 XOR은 매우 나쁜 생각입니다. –

+0

JodaTime이 암호화하는 동안 문제가 너무 많아서 시간 차이가 24 시간 미만인지 확인하고 싶다면 Calender를 사용할 수도 있습니다. –

+0

암호화를 사용하면 암호화가 모든 것을 상태 비 저장으로 만들지 만 원하니? 그렇지 않은 경우 데이터베이스에 현재 또는 최대 허용 시간을 저장하고 리셋 요청이 들어 왔을 때 비교하는 것이 어떻습니까? –

답변

2

이 클래스는 두 개의 공공 방법, 그것의 유효성을 확인하는 토큰과 다른를 생성하기위한 하나입니다. 훨씬 더 크고 복잡한 코드에서 요약되므로 일부 오류가 발생할 수 있습니다. 일부 테스트가 포함되어있어 즉시 테스트 할 수 있습니다. 어떤 방법 으로든, 나는 당신을 올바른 길로 인도하기에 충분할 것이라고 희망합니다.

package tokens; 

import java.nio.charset.StandardCharsets; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.util.Base64; 
import javax.crypto.Mac; 
import javax.crypto.spec.SecretKeySpec; 

public class TokenUtils { 

    private static final String HMAC_ALGO = "HmacSHA256"; 
    private static final String TOKEN_SEPARATOR = ":"; 
    private static final long MAX_AGE = 1_000 * 60 * 60 * 24; // 24h 

    private TokenUtils() { 
    } 

    public static String createToken(String username, long timestamp, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException { 
     StringBuilder sb = new StringBuilder(); 
     sb.append(generateTokenStringPublicPart(username, timestamp)); 
     sb.append(TOKEN_SEPARATOR); 
     sb.append(computeSignature(username, timestamp, secretKey)); 
     return sb.toString(); 
    } 

    public static boolean verifyToken(String token, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException { 
     String[] parts = token.split(TOKEN_SEPARATOR); 
     boolean result = false; 
     if (parts.length == 3) { 
      String username = parts[0]; 
      Long timestamp = Long.valueOf(parts[1]); 
      String signature = parts[2]; 
      if (signature.equals(computeSignature(username, timestamp, secretKey))) { 
       if (System.currentTimeMillis() - timestamp < MAX_AGE) { 
        result = true; 
       } 
      } 
     } 
     return result; 
    } 

    private static String generateTokenStringPublicPart(String username, long timestamp) { 
     StringBuilder sb = new StringBuilder(); 
     sb.append(username); 
     sb.append(TOKEN_SEPARATOR); 
     sb.append(timestamp); 
     return sb.toString(); 
    } 

    private static String computeSignature(String username, long timestamp, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException { 
     StringBuilder sb = new StringBuilder(); 
     sb.append(generateTokenStringPublicPart(username, timestamp)); 
     SecretKeySpec sks = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), HMAC_ALGO); 
     Mac hmac = Mac.getInstance(HMAC_ALGO); 
     hmac.init(sks); 
     return Base64.getUrlEncoder().encodeToString(hmac.doFinal(sb.toString().getBytes(StandardCharsets.UTF_8))); 
    } 

    public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException { 
     String secretKey = "secret_key"; 
     String token = TokenUtils.createToken("marko", System.currentTimeMillis(), secretKey); 
     System.out.println(token); 
     System.out.println("Original token verification: " + TokenUtils.verifyToken(token, secretKey)); 
     token = token.replaceAll("a", "b"); 
     System.out.println("Tampered token verification: " + TokenUtils.verifyToken(token, secretKey)); 
     token = TokenUtils.createToken("marko", System.currentTimeMillis() - 1_000 * 60 * 60 * 48, secretKey); 
     System.out.println("Expired token verification: " + TokenUtils.verifyToken(token, secretKey)); 
    } 

} 
+0

고맙습니다. 늦게 여기 있습니다 ... 나는 내일 아침에 제일 먼저 시도 할 것입니다. –

+0

그 코드가 많은 도움을 주셔서 감사합니다. 하나의 질문, 비밀 키에 관한 조언, 어떻게 생성해야하는지, 나는 UUID.randomUUID()를 사용하여 생각하고 있었다. .. –

+1

키를 변경하면 이전 키로 서명 된 모든 토큰이 무효화되므로 그것을 동일하게 유지하는 것이 합리적 일 것입니다. 좋은 long-ish 무작위 문자열 (예 :'dd if =/dev/urandom bs = 1k count = 1 | sha512sum')을 생성하고 하드 코딩 만하면됩니다. 무작위로 추측하는 것은 사실상 불가능합니다. –

관련 문제