Au fait, si vous utilisez normalement ssh-keygen
, cela ressemble à ce qui suit.
ssh-keygen -t rsa -b 4096
J'emprunterai l'explication de l'article suivant de Qiita.
(Emprunté d'ici)
Si les deux grands nombres premiers sont $ p $ et $ q $
N = pq \tag{1}
Soit $ L $ la variable qui satisfait (2) ci-dessous.
L = lcm(p-1, q-1) \tag{2}
De plus, soit $ E $ une variable qui satisfait l'équation suivante (3).
gcd(E, L) = 1 \tag{3}
Enfin, soit $ D $ la variable qui satisfait l'équation (4).
ED≡1 (mod L) \tag{4}
La paire $ (E, N) $ obtenue jusqu'à présent est la * clé publique *, et la paire $ (D, N) $ est la * clé privée *.
(Emprunté jusqu'à présent)
Comme je l'ai écrit au début, je suppose que le format PEM et aucune phrase de passe.
Fondamentalement, cela ressemble à PKCS # 8 au format PEM.
Pour plus de détails, voir «Format PKCS # 8» dans «Format de fichier de clé publique RSA et empreinte digitale» ci-dessus.
À propos, pour les instances Java RSAPrivateKey, la méthode getEncoded ()
est utilisée pour obtenir les données binaires, vous devriez donc être capable de faire un encodage Base64 de type MIME.
public Optional<String> encodePrivateKey(final PrivateKey privateKey) {
final String keyType;
if (privateKey instanceof RSAPrivateKey) {
keyType = "RSA";
} else if (privateKey instanceof DSAPrivateKey) {
keyType = "DSA";
} else if (privateKey instanceof ECPrivateKey) {
keyType = "EC";
} else {
return Optional.empty();
}
final byte[] privateBytes = privateKey.getEncoded();
final String privateBase64 = Base64.getMimeEncoder()
.encodeToString(privateBytes);
final String encodedPrivateKey = "-----BEGIN " + keyType + " PRIVATE KEY-----\n"
+ privateBase64 + "\n"
+ "-----END " + keyType + " PRIVATE KEY-----";
return Optional.of(encodedPrivateKey);
}
Contrairement au format décrit dans RFC4716, dans le cas du format OpenSSH, il n'y a pas de délimiteur "BEGIN" / "END" avant et après, mais au début. Il a la signature "ssh-rsa".
Les données suivantes sont similaires au format RFC4716 (de l'article «RSA Public Key File Format and fingerprint» ci-dessus).
Les détails de ce format peuvent être trouvés dans "6.6. Algorithmes de clé publique" dans RFC4253.
string "ssh-rsa" mpint e mpint n
Le format de cette "chaîne" ou "mpint" est décrit dans "5. Représentations des types de données utilisées dans les protocoles SSH" de RFC4251. En termes simples, c'est comme une longueur de 4 octets + une chaîne d'octets.
Il semble que cela devrait être encodé en Base64. Dans le cas du format OpenSSH, Base64 semble devoir sortir une ligne sans encapsuler la ligne.
$ e $ et $ n $ correspondent à $ E $ et $ N $ décrits dans la "Présentation des paires de clés RSA" ci-dessus.
Au fait, les valeurs de $ e $ et $ n $ peuvent être obtenues par la méthode getPublicExponent ()
et la méthode getModulus ()
, respectivement, pour l'instance Java RSAPublicKey.
(Parfois, la clé publique a quelque chose comme une adresse e-mail à la fin, mais cela devrait être un commentaire)
string" ssh-rsa "
ci-dessus), et séparément avant base64. (La partie que j'ai dite au début, "Au lieu de n'avoir aucune délimitation, elle a la signature" ssh-rsa "au début"). Je pense que c'est plus facile à comprendre si vous regardez la source suivante. public String encodeRsaPublicKey(final RSAPublicKey publicKey) {
final String sig = "ssh-rsa";
final byte[] sigBytes = sig.getBytes(StandardCharsets.US_ASCII);
final byte[] eBytes = publicKey.getPublicExponent().toByteArray();
final byte[] nBytes = publicKey.getModulus().toByteArray();
final int size = 4 + sigBytes.length
+ 4 + eBytes.length
+ 4 + nBytes.length;
final byte[] publicKeyBytes = ByteBuffer.allocate(size)
.order(ByteOrder.BIG_ENDIAN)
.putInt(sigBytes.length).put(sigBytes)
.putInt(eBytes.length).put(eBytes)
.putInt(nBytes.length).put(nBytes)
.array();
final String publicKeyBase64 = Base64.getEncoder()
.encodeToString(publicKeyBytes);
final String publicKeyEncoded = sig + " " + publicKeyBase64;
return publicKeyEncoded;
}
DSA est décrit dans RFC4253 ainsi que RSA.
string "ssh-dss" mpint p mpint q mpint g mpint y
public String encodeDsaPublicKey(final DSAPublicKey publicKey) {
final String sig = "ssh-dss";
final byte[] sigBytes = sig.getBytes(StandardCharsets.US_ASCII);
final DSAParams params = publicKey.getParams();
final byte[] pBytes = params.getP().toByteArray();
final byte[] qBytes = params.getQ().toByteArray();
final byte[] gBytes = params.getG().toByteArray();
final byte[] yBytes = publicKey.getY().toByteArray();
final int size = 4 + sigBytes.length
+ 4 + pBytes.length
+ 4 + qBytes.length
+ 4 + gBytes.length
+ 4 + yBytes.length;
final byte[] publicKeyBytes = ByteBuffer.allocate(size)
.order(ByteOrder.BIG_ENDIAN)
.putInt(sigBytes.length).put(sigBytes)
.putInt(pBytes.length).put(pBytes)
.putInt(qBytes.length).put(qBytes)
.putInt(gBytes.length).put(gBytes)
.putInt(yBytes.length).put(yBytes)
.array();
final String publicKeyBase64 = Base64.getEncoder()
.encodeToString(publicKeyBytes);
final String publicKeyEncoded = sig + " " + publicKeyBase64;
return publicKeyEncoded;
}
Le format de la clé publique du code de la courbe elliptique semble être défini dans la RFC5656.
string "ecdsa-sha2-[identifier]"
byte[n] ecc_key_blob
ʻEcc_key_blob` est à l'intérieur
string [identifier]
string Q
«Identifiant» semble être obtenu à partir du type de courbe elliptique. La partie NIST du tableau ci-dessous est «identifiant».
NIST | SEC | OID |
---|---|---|
nistp256 | secp256r1 | 1.2.840.10045.3.1.7 |
nistp384 | secp384r1 | 1.3.132.0.34 |
nistp521 | secp521r1 | 1.3.132.0.35 |
En passant, ces trois types semblent être des courbes [obligatoires](une courbe elliptique qui doit être implémentée lorsque ECDSA est pris en charge par les serveurs et clients SSH).
Il semble qu'il existe d'autres courbes elliptiques [recommandées], mais je les omettrai car elles sont nombreuses.
Au fait, la courbe recommandée ʻidentifier utilise la colonne OID au lieu de la colonne NIST (exemple: il devrait s'agir d'une signature comme ʻecdsa-sha2-1.3.132.0.38
). Voir RFC pour plus de détails.
De plus, il semble que Q
soit" un point codé sur une courbe elliptique dans une chaîne de caractères d'octet ", mais les détails de la spécification sont` http://www.secg.org/download/aid-780/sec1-v2 Il est mentionné en .pdf ».
Quand je l'ai recherché, il semble qu'il soit passé à https://www.secg.org/sec1-v2.pdf.
Plus d'informations peuvent être trouvées dans 2.3.3 de ce document.
Il semble que le format diffère selon que vous utilisez ou non la fonction appelée "compression ponctuelle", mais si vous ne l'utilisez pas, cela semble être une structure relativement simple.
Pour le moment, en supposant que «identificateur» a été décidé, il ressemble à ce qui suit.
public String encodeEcPublicKey(
final String identifier,
final ECPublicKey publicKey) {
final String sig = "ecdsa-sha2-" + identifier;
final BigInteger x = publicKey.getW().getAffineX();
final BigInteger y = publicKey.getW().getAffineY();
final byte[] sigBytes = sig.getBytes(StandardCharsets.US_ASCII);
final byte[] identifierBytes = identifier.getBytes(StandardCharsets.US_ASCII);
final byte[] xBytes = x.toByteArray();
final byte[] yBytes = y.toByteArray();
final int size = 4 + sigBytes.length
+ 4 + identifierBytes.length
+ 4 + 1 + xBytes.length + yBytes.length;
final byte[] publicKeyBytes = ByteBuffer.allocate(size)
.order(ByteOrder.BIG_ENDIAN)
.putInt(sigBytes.length).put(sigBytes)
.putInt(identifierBytes.length).put(identifierBytes)
.putInt(1 + xBytes.length + yBytes.length)
.put((byte) 0x04)
.put(xBytes)
.put(yBytes)
.array();
final String publicKeyBase64 = Base64.getEncoder()
.encodeToString(publicKeyBytes);
final String publicKeyEncoded = sig + " " + publicKeyBase64;
return publicKeyEncoded;
}
Et ʻidentifier indique le type de courbe elliptique, et il semble que le contenu de ʻECParameterSpec
qui peut être obtenu à partir de la clé privée ou de la clé publique puisse être utilisé pour le jugement.
Pour des paramètres spécifiques, il semble nécessaire de voir les spécifications de la courbe elliptique.
Les détails des spécifications peuvent être consultés sur «http: // www.secg.org / download / aid-386 / sec2_final.pdf». Quand je l'ai recherché, il a également été déplacé https://www.secg.org/SEC2-Ver-1.0.pdf C'était en.
Pour le moment, pour déterminer la courbe requise ʻidentifier basée sur ʻECParameterSpec
, cela ressemble à ce qui suit.
private Optional<String> decideEcIdentifier(final ECParameterSpec params) {
if (!(params.getCurve().getField() instanceof ECFieldFp)) {
return Optional.empty();
}
final ECFieldFp field = (ECFieldFp) params.getCurve().getField();
final BigInteger p = field.getP();
final BigInteger a = params.getCurve().getA();
final BigInteger b = params.getCurve().getB();
final BigInteger g1 = params.getGenerator().getAffineX();
final BigInteger g2 = params.getGenerator().getAffineY();
final BigInteger n = params.getOrder();
final int h = params.getCofactor();
final List<Number> tuple = Arrays.asList(p, a, b, g1, g2, n, h);
final String identifier = identifierMap.get(tuple);
return Optional.ofNullable(identifier);
}
private static final Map<List<Number>, String> identifierMap;
static {
final Map<List<Number>, String> map = new LinkedHashMap<>();
map.put(Arrays.asList(
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951"),
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291"),
new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109"),
new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"),
1), "nistp256");
map.put(Arrays.asList(
new BigInteger(
"39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319"),
new BigInteger(
"39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112316"),
new BigInteger(
"27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575"),
new BigInteger(
"26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087"),
new BigInteger(
"8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871"),
new BigInteger(
"39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643"),
1), "nistp384");
map.put(Arrays.asList(
new BigInteger(
"6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"),
new BigInteger(
"6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057148"),
new BigInteger(
"1093849038073734274511112390766805569936207598951683748994586394495953116150735016013708737573759623248592132296706313309438452531591012912142327488478985984"),
new BigInteger(
"2661740802050217063228768716723360960729859168756973147706671368418802944996427808491545080627771902352094241225065558662157113545570916814161637315895999846"),
new BigInteger(
"3757180025770020463545507224491183603594455134769762486694567779615544477440556316691234405012945539562144444537289428522585666729196580810124344277578376784"),
new BigInteger(
"6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449"),
1), "nistp521");
identifierMap = Collections.unmodifiableMap(map);
}
Ceci est un exemple d'encodage des trois formats ci-dessus.
public Optional<String> encodePublicKey(final PublicKey publicKey) {
if (publicKey instanceof RSAPublicKey) {
final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
return Optional.of(encodeRsaPublicKey(rsaPublicKey));
} else if (publicKey instanceof DSAPublicKey) {
final DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
return Optional.of(encodeDsaPublicKey(dsaPublicKey));
} else if (publicKey instanceof ECPublicKey) {
final ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
return decideEcIdentifier(ecPublicKey.getParams())
.map(identifier -> encodeEcPublicKey(identifier, ecPublicKey));
}
return Optional.empty();
}
Si vous utilisez ssh-keygen
, cela ressemble à ce qui suit
ssh-keygen -yf id_rsa > id_rsa.pub
Si vous le faites en Java, cela ressemble à ceci.
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(4096);
KeyPair keyPair = generator.genKeyPair();
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
BigInteger publicExponent = privateKey.getPublicExponent();
BigInteger modulus = privateKey.getModulus();
KeyFactory factory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec spec = new RSAPublicKeySpec(
modulus,
publicExponent);
RSAPublicKey publicKey = (RSAPublicKey) factory.generatePublic(spec);
Le fait est que la clé privée RSA normalement générée implémente également RSAPrivateCrtKey
.
ED25519 est-il une RFC pas encore publiée?
Projet Internet [https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-ed448-08](https://tools.ietf.org/html/draft-ietf-curdle-ssh -ed25519-ed448-08) ressemble à ce qui suit.
string "ssh-ed25519" string key
De même, ED448 ressemble à ce qui suit.
string "ssh-ed448" string key
Recommended Posts