Übrigens, wenn Sie normalerweise "ssh-keygen" verwenden, sieht es wie folgt aus.
ssh-keygen -t rsa -b 4096
Ich werde die Erklärung aus dem folgenden Qiita-Artikel ausleihen.
(Von hier ausgeliehen)
Wenn die beiden großen Primzahlen $ p $ und $ q $ sind
N = pq \tag{1}
Sei $ L $ die Variable, die (2) unten erfüllt.
L = lcm(p-1, q-1) \tag{2}
Außerdem sei $ E $ eine Variable, die die folgende Gleichung (3) erfüllt.
gcd(E, L) = 1 \tag{3}
Schließlich sei $ D $ die Variable, die Gleichung (4) erfüllt.
ED≡1 (mod L) \tag{4}
Das bisher erhaltene $ (E, N) $ -Paar ist der * öffentliche Schlüssel *, und das $ (D, N) $ -Paar ist der * private Schlüssel *.
(Bisher ausgeliehen)
Wie ich am Anfang geschrieben habe, nehme ich das PEM-Format und keine Passphrase an.
Grundsätzlich sieht es aus wie PKCS # 8 im PEM-Format.
Weitere Informationen finden Sie oben unter "PKCS # 8-Format" unter "RSA-Dateiformat mit öffentlichem Schlüssel und Fingerabdruck".
Übrigens wird für Java RSAPrivateKey-Instanzen die Methode "getEncoded ()" verwendet, um die Binärdaten abzurufen, sodass Sie in der Lage sein sollten, eine MIME-ähnliche Base64-Codierung durchzuführen.
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);
}
Im Gegensatz zu dem in RFC4716 beschriebenen Format gibt es im OpenSSH-Format kein "BEGIN" / "END" -Trennzeichen davor und danach, sondern am Anfang. Es hat die Signatur "ssh-rsa".
Nachfolgende Daten ähneln dem RFC4716-Format (aus dem obigen Artikel "RSA Public Key-Dateiformat und Fingerabdruck").
Details zu diesem Format finden Sie unter "6.6. Algorithmen für öffentliche Schlüssel" in RFC4253.
string "ssh-rsa" mpint e mpint n
Das Format dieses "Strings" oder "mpint" ist in "5. In den SSH-Protokollen verwendete Datentypdarstellungen" von RFC4251 beschrieben. Einfach ausgedrückt ist es wie eine 4-Byte-Big-Endian-Länge + Byte-Zeichenfolge.
Es scheint, dass dies in Base64 codiert werden sollte. Im Fall des OpenSSH-Formats muss Base64 anscheinend eine Zeile ausgeben, ohne die Zeile zu umbrechen.
$ e $ und $ n $ entsprechen $ E $ und $ N $, die oben in der "Übersicht über RSA-Schlüsselpaare" beschrieben wurden.
Übrigens können die Werte von $ e $ und $ n $ mit der Methode "getPublicExponent ()" bzw. der Methode "getModulus ()" für die Java RSAPublicKey-Instanz abgerufen werden.
(Manchmal hat der öffentliche Schlüssel am Ende so etwas wie eine E-Mail-Adresse, aber dies sollte ein Kommentar sein.)
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 wird in RFC4253 sowie in RSA beschrieben.
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;
}
Das Format des öffentlichen Schlüssels des Codes für die elliptische Kurve scheint in RFC5656 definiert zu sein.
string "ecdsa-sha2-[identifier]"
byte[n] ecc_key_blob
Der Inhalt von ecc_key_blob
ist
string [identifier]
string Q
"Bezeichner" scheint aus der Art der elliptischen Kurve erhalten zu werden. Der NIST-Teil der folgenden Tabelle ist der "Bezeichner".
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 |
Übrigens scheinen diese drei Typen [erforderliche] Kurven zu sein (eine elliptische Kurve, die implementiert werden muss, wenn ECDSA von SSH-Servern und -Clients unterstützt wird).
Es scheint, dass es andere [empfohlene] elliptische Kurven gibt, aber ich werde sie weglassen, weil es viele gibt. Übrigens verwendet der empfohlene Kurven-Bezeichner die OID-Spalte anstelle der NIST-Spalte (z. B. sollte es sich um eine Signatur wie "ecdsa-sha2-1.3.132.0.38" handeln). Siehe RFC für Details.
Es scheint auch, dass "Q" "ein codierter Punkt auf einer elliptischen Kurve in eine Oktettzeichenfolge" ist, aber die Details der Spezifikation sind "http://www.secg.org/download/aid-780/sec1-v2" Es wird in .pdf` erwähnt. Als ich tatsächlich danach gesucht habe, scheint es, dass es zu https://www.secg.org/sec1-v2.pdf verschoben wurde. Weitere Informationen finden Sie in 2.3.3 dieses Dokuments. Es scheint, dass das Format unterschiedlich ist, je nachdem, ob Sie die Funktion "Punktkomprimierung" verwenden oder nicht. Wenn Sie sie jedoch nicht verwenden, scheint es sich um eine relativ einfache Struktur zu handeln.
Unter der Annahme, dass "Bezeichner" entschieden wird, sieht es vorerst wie folgt aus.
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;
}
Und "Bezeichner" gibt den Typ der elliptischen Kurve an, und es scheint, dass die Beurteilung durch den Inhalt von "ECParameterSpec" erfolgen kann, der aus dem privaten oder öffentlichen Schlüssel erhalten werden kann.
Für bestimmte Parameter scheint es notwendig zu sein, die Spezifikationen der elliptischen Kurve zu sehen.
Details zu den Spezifikationen finden Sie unter "http: // www.secg.org / download / aid-386 / sec2_final.pdf". Als ich tatsächlich danach suchte, wurde es auch verschoben https://www.secg.org/SEC2-Ver-1.0.pdf Es war in.
Um die erforderliche Kurven-ID auf der Grundlage von ECParameterSpec zu bestimmen, sieht es vorerst wie folgt aus.
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);
}
Dies ist ein Beispiel für die Codierung der oben genannten drei Formate.
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();
}
Wenn Sie ssh-keygen
verwenden, sieht es wie folgt aus
ssh-keygen -yf id_rsa > id_rsa.pub
Wenn Sie es in Java tun, sieht es so aus.
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);
Der Punkt ist, dass der normalerweise generierte private RSA-Schlüssel auch "RSAPrivateCrtKey" implementiert.
Ist ED25519 ein RFC, der noch nicht veröffentlicht wurde?
Internet-Entwurf [https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-ed448-08](https://tools.ietf.org/html/draft-ietf-curdle-ssh -ed25519-ed448-08) sieht wie folgt aus.
string "ssh-ed25519" string key
In ähnlicher Weise sieht ED448 wie folgt aus.
string "ssh-ed448" string key
Recommended Posts