I am trying to implement Log in with Telegram ( https://core.telegram.org/widgets/login ) on my Spring Boot application, but faced a problem.
I've been trying to implement PHP code they provided to verify authentication, but something is wrong and I can't understand what.
So, that's the code on PHP
secret_key = SHA256(<bot_token>)
if (hex(HMAC_SHA256(data_check_string, secret_key)) == hash) {
// data is from Telegram
}
Data-check-string is a concatenation of all received fields, sorted in alphabetical order, in the format key=<value>
with a line feed character ('\\n', 0xA0) used as separator – eg, 'auth_date=<auth_date>\\nfirst_name=<first_name>\\nid=<id>\\nusername=<username>
.
So, what I did is:
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class AuthenticationRequest {
@NotNull
private Long authDate;
private String firstName;
@NotEmpty
private String id;
private String lastName;
private String photoUrl;
private String username;
@NotEmpty
private String hash;
@Override
public String toString() {
final var data = new StringBuilder();
for (final Field field : getClass().getDeclaredFields()) {
try {
if (!field.getName().equals("hash") && field.get(this) != null) {
final var fieldName = CaseFormat.LOWER_CAMEL
.to(CaseFormat.LOWER_UNDERSCORE, field.getName());
data.append(fieldName).append("=").append(field.get(this)).append("\\n");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return data.substring(0, data.length() - 2);
}
}
And these two methods:
private static String hmacSha256(final String data, final byte[] secret) {
try {
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret, "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] signedBytes = sha256Hmac.doFinal(data.getBytes());
return bytesToHex(signedBytes);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
return null;
}
}
private static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder();
for (final byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
And when I compare them, they are completely different two strings
final var telegramData = authenticationRequest.toString();
final var digest = MessageDigest.getInstance("SHA-256");
final var hashedToken = digest.digest(botToken.getBytes());
System.out.println(authenticationRequest.getHash());
System.out.println(hmacSha256(telegramData, hashedToken));
Could you please give me a hint on what I am doing wrong? Maybe I completely misunderstood the way I have to validate the authentication data, or maybe I missed something?
try this, the code is kind of ugly, but it worked well!
public boolean verifyAuth(JsonObject Telegram_User){
String hash = Telegram_User.remove("hash").getAsString();
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
String[] t = Telegram_User.toString().replace("{","").replace("}","").replace("\":","=").replace(",","\n").replace("\"","").split("\n");
sha256_HMAC.init(new SecretKeySpec(MessageDigest.getInstance("SHA-256").digest(BezouroBot.telegram.getBotToken().getBytes(StandardCharsets.UTF_8)),"SHA256"));
Arrays.sort(t);
StringBuilder i = new StringBuilder();
boolean First = true;
for (String s : t) if(First){ First = false; i = new StringBuilder(s);} else i.append("\n").append(s);
return Hex.encodeHexString(sha256_HMAC.doFinal(i.toString().getBytes())).equals(hash);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
return false;
}
}
Here is my implement
// define your token to a variable
private final String TELEGRAM_TOKEN = ""
@PostMapping("auth/telegram")
public ResponseEntity<Object> telegramAuth(@RequestBody Map<String, Object> request) {
String hash = (String) request.get("hash");
request.remove("hash");
// Prepare the string
String str = request.entrySet().stream()
.sorted((a, b) -> a.getKey().compareToIgnoreCase(b.getKey()))
.map(kvp -> kvp.getKey() + "=" + kvp.getValue())
.collect(Collectors.joining("\n"));
try {
SecretKeySpec sk = new SecretKeySpec(
// Get SHA 256 from telegram token
MessageDigest.getInstance("SHA-256").digest(TELEGRAM_TOKEN.getBytes(StandardCharsets.UTF_8)
), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(sk);
byte[] result = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
// Convert the result to hex string
// Like https://stackoverflow.com/questions/9655181
String resultStr = ByteBufUtil.bytesToHex(result);
// Compare the result with the hash from body
if(hash.compareToIgnoreCase(resultStr) == 0) {
// Do other things like create a user and JWT token
return ResponseEntity.ok("ok");
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
new MessageResponse("Login info hash mismatch")
);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
new MessageResponse("Server error while authenticating")
);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.