Signer.java
// SPDX-License-Identifier: Apache-2.0
package com.shengzhiai.yugu.stcompat.internal;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* Platform signature (CONTRACT 0.2, DESIGN 5.2): drop null and empty values, sort keys
* lexicographically, join as {@code k=v&k=v} without URL encoding, then
* {@code Base64(HMAC_SHA256(payload, secretKey))}. For compat REST the signed set is exactly the
* text form fields sent; the audio file part is not signed.
*/
public final class Signer {
private Signer() {
}
public static String payload(Map<String, String> params) {
TreeMap<String, String> sorted = new TreeMap<String, String>();
if (params != null) {
for (Map.Entry<String, String> e : params.entrySet()) {
if (e.getKey() != null && e.getValue() != null && e.getValue().length() > 0) {
sorted.put(e.getKey(), e.getValue());
}
}
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> e : sorted.entrySet()) {
if (sb.length() > 0) {
sb.append('&');
}
sb.append(e.getKey()).append('=').append(e.getValue());
}
return sb.toString();
}
public static String sign(Map<String, String> params, String secretKey) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secretKey.getBytes(Codec.UTF_8), "HmacSHA256"));
return Codec.base64(mac.doFinal(payload(params).getBytes(Codec.UTF_8)));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("HmacSHA256 not available", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("invalid secretKey", e);
}
}
}