基本的なアクティベーション方法
Charlesでアクティベーションを行うには、メニューから Help -> Register Charles を選択します。
表示されたダイアログの
Registered Nameに任意の名前を、生成されたLicense keyに入力してRegisterをクリックします。
以下に、このライセンスキーを計算するためのJavaおよびGoによる実装例を示します。
Java による実装
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;
import java.util.Scanner;
public class CharlesLicenseGenerator {
private static final int ROUNDS = 12;
private static final int ROUND_KEYS = 2 * (ROUNDS + 1);
private static final Random RANDOM = new Random();
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String userName = scanner.nextLine().toLowerCase();
RANDOM.setSeed(System.nanoTime());
String licenseKey = generateKey(userName);
System.out.println("Registered Name: " + userName + " License Key: " + licenseKey);
}
private static String generateKey(String inputText) {
byte[] nameBytes = inputText.getBytes();
int nameLength = nameBytes.length + 4;
int paddedLength = ((-nameLength) & (8 - 1)) + nameLength;
ByteBuffer inputBuffer = ByteBuffer.allocate(paddedLength);
inputBuffer.order(ByteOrder.BIG_ENDIAN);
inputBuffer.putInt(nameBytes.length);
inputBuffer.put(nameBytes);
long cipherKey1 = 0x7a21c951691cd470L;
long cipherKey2 = -5408575981733630035L;
CkCipher cipher = new CkCipher(cipherKey1);
ByteBuffer outputBuffer = ByteBuffer.allocate(paddedLength);
outputBuffer.order(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < paddedLength; i += 8) {
long currentBlock = inputBuffer.getLong(i);
long encryptedBlock = cipher.encrypt(currentBlock);
outputBuffer.putLong(encryptedBlock);
}
int checksum = 0;
for (byte b : outputBuffer.array()) {
checksum = rotateLeft(checksum ^ (int) b, 3);
}
int prefix = checksum ^ 0x54882f8a;
int suffix = getSuffix();
long combinedValue = ((long) prefix << 32) | (suffix & 0xffffffffL);
long decryptedValue = new CkCipher(cipherKey2).decrypt(combinedValue);
long mask = 0;
for (int i = 56; i >= 0; i -= 8) {
mask ^= (combinedValue >> i) & 0xff;
}
int verificationByte = (int) (mask & 0xff);
if (verificationByte < 0) {
verificationByte = -verificationByte;
}
return String.format("%02x%016x", verificationByte, decryptedValue);
}
private static int getSuffix() {
int randomSuffix = RANDOM.nextInt();
int suffixType = (randomSuffix >> 16);
if (suffixType == 0x0401 || suffixType == 0x0402 || suffixType == 0x0403) {
return randomSuffix;
} else {
return (randomSuffix & 0xffffff) | 0x01000000;
}
}
private static class CkCipher {
private int[] roundKeys = new int[ROUND_KEYS];
public CkCipher(long cipherKey) {
int[] keyData = new int[]{(int) cipherKey, (int) (cipherKey >>> 32)};
roundKeys[0] = -1209970333;
for (int i = 1; i < ROUND_KEYS; i++) {
roundKeys[i] = roundKeys[i - 1] - 1640531527;
}
int a = 0, b = 0, keyIndex = 0, dataIndex = 0;
for (int k = 0; k < 3 * ROUND_KEYS; k++) {
roundKeys[keyIndex] = rotateLeft(roundKeys[keyIndex] + (a + b), 3);
a = roundKeys[keyIndex];
keyData[dataIndex] = rotateLeft(keyData[dataIndex] + (a + b), a + b);
b = keyData[dataIndex];
keyIndex = (keyIndex + 1) % ROUND_KEYS;
dataIndex = (dataIndex + 1) % 2;
}
}
public long encrypt(long input) {
int a = (int) input + roundKeys[0];
int b = (int) (input >>> 32) + roundKeys[1];
for (int r = 1; r <= ROUNDS; r++) {
a = rotateLeft(a ^ b, b) + roundKeys[2 * r];
b = rotateLeft(b ^ a, a) + roundKeys[2 * r + 1];
}
return combine(a, b);
}
public long decrypt(long input) {
int a = (int) input;
int b = (int) (input >>> 32);
for (int i = ROUNDS; i > 0; i--) {
b = rotateRight(b - roundKeys[2 * i + 1], a) ^ a;
a = rotateRight(a - roundKeys[2 * i], b) ^ b;
}
b -= roundKeys[1];
a -= roundKeys[0];
return combine(a, b);
}
}
private static int rotateLeft(int value, int shift) {
return (value << (shift & 31)) | (value >>> (32 - (shift & 31)));
}
private static int rotateRight(int value, int shift) {
return (value >>> (shift & 31)) | (value << (32 - (shift & 31)));
}
private static long combine(int high, int low) {
return ((long) high & 0xffffffffL) | ((long) low << 32);
}
}
Go による実装
package main
import (
"bytes"
"encoding/binary"
"fmt"
"math/rand"
"time"
)
const (
roundCount = 12
roundKeyCount = 2 * (roundCount + 1)
)
func main() {
rand.Seed(time.Now().UnixNano())
targetName := "Charles"
key := computeLicenseKey(targetName)
fmt.Println("Registered Name:", targetName, " License Key:", key)
}
func computeLicenseKey(input string) string {
nameBytes := []byte(input)
payloadLen := len(nameBytes) + 4
paddedLen := ((-payloadLen) & (8 - 1)) + payloadLen
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, uint32(len(nameBytes)))
buf.Write(nameBytes)
var cipherConstant1 int64 = 0x7a21c951691cd470
var cipherConstant2 int64 = -5408575981733630035
cipher := newCipher(cipherConstant1)
encryptedBuf := new(bytes.Buffer)
for i := 0; i < paddedLen; i += 8 {
blockBytes := buf.Bytes()[i : i+8]
blockReader := bytes.NewReader(blockBytes)
var block int64
binary.Read(blockReader, binary.BigEndian, &block)
encryptedBlock := cipher.encrypt(block)
encryptedBuf.WriteByte(byte(encryptedBlock >> 56))
encryptedBuf.WriteByte(byte(encryptedBlock >> 48))
encryptedBuf.WriteByte(byte(encryptedBlock >> 40))
encryptedBuf.WriteByte(byte(encryptedBlock >> 32))
encryptedBuf.WriteByte(byte(encryptedBlock >> 24))
encryptedBuf.WriteByte(byte(encryptedBlock >> 16))
encryptedBuf.WriteByte(byte(encryptedBlock >> 8))
encryptedBuf.WriteByte(byte(encryptedBlock))
}
var checksum int32
for _, b := range encryptedBuf.Bytes() {
checksum = rotateLeft(checksum^int32(int8(b)), 3)
}
prefix := checksum ^ 0x54882f8a
suffix := generateSuffix()
combined := int64(prefix)<<32 | int64(suffix)
decrypted := newCipher(cipherConstant2).decrypt(combined)
var mask int64
for i := 56; i >= 0; i -= 8 {
mask ^= int64((uint64(combined) >> i) & 0xff)
}
verification := int32(mask & 0xff)
if verification < 0 {
verification = -verification
}
return fmt.Sprintf("%02x%016x", verification, uint64(decrypted))
}
func generateSuffix() int32 {
randomValue := rand.Int31()
suffixType := randomValue >> 16
switch suffixType {
case 0x0401, 0x0402, 0x0403:
return randomValue
default:
return (randomValue & 0xffffff) | 0x01000000
}
}
type cipher struct {
roundKeys [roundKeyCount]int32
}
func newCipher(key int64) cipher {
c := cipher{}
var keyParts [2]int32
keyParts[0] = int32(key)
keyParts[1] = int32(uint64(key) >> 32)
c.roundKeys[0] = -1209970333
for i := 1; i < roundKeyCount; i++ {
c.roundKeys[i] = c.roundKeys[i-1] + -1640531527
}
var a, b int32
var keyIndex, partIndex int
for iteration := 0; iteration < 3*roundKeyCount; iteration++ {
c.roundKeys[keyIndex] = rotateLeft(c.roundKeys[keyIndex]+(a+b), 3)
a = c.roundKeys[keyIndex]
keyParts[partIndex] = rotateLeft(keyParts[partIndex]+(a+b), a+b)
b = keyParts[partIndex]
keyIndex = (keyIndex + 1) % roundKeyCount
partIndex = (partIndex + 1) % 2
}
return c
}
func (c cipher) encrypt(input int64) int64 {
a := int32(input) + c.roundKeys[0]
b := int32(uint64(input)>>32) + c.roundKeys[1]
for r := 1; r <= roundCount; r++ {
a = rotateLeft(a^b, b) + c.roundKeys[2*r]
b = rotateLeft(b^a, a) + c.roundKeys[2*r+1]
}
return combine(a, b)
}
func (c cipher) decrypt(input int64) int64 {
a := int32(input)
b := int32(uint64(input) >> 32)
for i := roundCount; i > 0; i-- {
b = rotateRight(b-c.roundKeys[2*i+1], a) ^ a
a = rotateRight(a-c.roundKeys[2*i], b) ^ b
}
b -= c.roundKeys[1]
a -= c.roundKeys[0]
return combine(a, b)
}
func rotateLeft(value int32, shift int32) int32 {
return int32(value<<(shift&(32-1))) | int32(uint32(value)>>(32-(shift&(32-1))))
}
func rotateRight(value int32, shift int32) int32 {
return int32(uint32(value)>>(shift&(32-1))) | int32(value<<(32-(shift&(32-1))))
}
func combine(high int32, low int32) int64 {
return (int64(high) & 0xffffffff) | (int64(low) << 32)
}
オンラインツール
以下のURLで、ブラウザから直接ライセンスキーを生成できるオンラインツールも利用可能です。
https://www.zzzmode.com/mytools/charles/
本稿の内容は、技術的な学習と理解を目的としたものです。ライセンスの不正利用は法律に違反する可能性があります。正規ライセンスの購入を強く推奨します。