O char, apesar do nome, é um tipo numérico, e seus valores vão de zero a 65535 (conforme especificação da linguagem). Esse número representa o Unicode Code Point do caractere.
De forma bem resumida, o Unicode define que todo caractere possui um valor numérico associado, chamado de code point. No caso dos primeiros 128 valores (ou seja, de zero a 127) os valores são os mesmos da tabela ASCII. Para mais detalhes, ver a documentação.
Enfim, o que acontece é que este número pode ser interpretado/manipulado tanto como um caractere quanto como o seu respectivo valor numérico (são basicamente “intercambiáveis”). Exemplo:
char c = 'a';
System.out.println(c); // imprime "a"
// mas um char também é um número
System.out.println((int) c); // 97 (o valor do caractere "a" na tabela ASCII)
// sendo número, nada impede de somar
c += 3; // soma 3
System.out.println((int) c); // 100
// mas 100 é o valor de outro caractere na tabela ASCII
System.out.println(c); // imprime "d"
//-----------------
// O contrário também é possível: pegar um int e interpretar como caractere
int n = 31021;
System.out.println(n); // imprime o número 31021
System.out.println((char) n); // imprime o caractere correspondente: 礭
O que acontece é que o método println, ao receber um int, imprime o valor numérico, e ao receber um char, imprime o caractere correspondente. Mas o fato de apresentar esta informação de forma diferente não muda o fato de que o char é um tipo numérico, e portanto seu valor pode ser manipulado como tal.
Então quando você faz int ascii = (int) caracteres[i] + cifra;, está pegando o char (no caso, caracteres[i], ou seja, o caractere que está na posição i do array caracteres) e somando cifra. Como um char também é um número, e vc está somando outro número (cifra), o resultado também será um número (a variável ascii).
No caso, vc está somando um char com um int, e pelas regras da linguagem, o resultado é um int. Então na verdade nem precisaria do cast, poderia ser apenas int ascii = caracteres[i] + cifra;. Ou ainda, como o programa só está imprimindo e não usa a variável ascii para mais nada, poderia ser apenas System.out.print((char) (caracteres[i] + cifra)).
Como cifra é 1, o programa imprime o “caractere seguinte”, ou seja, “a” vira “b”, “f” vira “g”, etc (o detalhe é que o “z” vai virar “{”, ou seja, nem tudo vai virar outra letra).