Domanda

Ho una chiave privata RSK PKCS # 5 crittografata PKCS # 5 memorizzata in un file su disco (originariamente generato da SSLPlus, circa 1997), ad esempio:

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICmDAaBgkqhkiG9w0BBQMwDQQIybM2XFqx4EwCAQUEggJ4MKg/NE+L6NJgbOf4
...
8QnGu4R7lFlweH/VAK8n0L75h3q2g62MKLJqmKLtAILNve4zymnO+LVZ4Js=
-----END ENCRYPTED PRIVATE KEY-----

Per cui ho bisogno di ottenere un oggetto Chiave Java che posso quindi aggiungere insieme al certificato corrispondente a un KeyStore. La chiave privata è crittografata con una chiave binaria da 100 byte.

La creazione di un oggetto Certificato è stata semplice, ma non riesco a capire come passare dalla chiave PKCS # 5 con codifica Base64 sopra alla chiave privata RSA decodificata PKCS # 8. A questo punto sono stymied perché la chiamata SecretKeyFactory.generateSecret () non riesce con:

InvalidKeySpecException: Password is not ASCII

Ora, è vero che la password non è ASCII, nel senso più stretto di essere da 0x00 a 0x7F, ma l'algoritmo PBEWithMD5AndDES dovrebbe accettare valori di carattere compresi tra 0x00 e 0xFF.

Qualcuno può mostrarmi come ottenere dal valore codificato Base64 a un oggetto Key che posso aggiungere a un keystore?


Conclusione

Il PBEKey emesso con Java accetta una password con valori ASCII nell'intervallo 0x20 < = char < = 0x7E. Questo problema con la mia password non ASCII è stato risolto creando il mio BinaryPBEKey che consentiva valori di byte da 0x00 a 0xFF (vedi sotto).

Il successivo problema che ho avuto è stato che i miei dati PKCS # 8 non sono stati codificati correttamente (apparentemente un errore comune con le prime implementazioni di SSL), in quanto i dati PKCS # 1 dovevano essere racchiusi in una stringa di ottetti ASN.1. Ho scritto una semplice routine di patching che tratterà delle mie chiavi, che sono note per avere una lunghezza compresa tra 512 e 4096 bit (vedi sotto).


Decodificatore chiave privata

private PrivateKey readPrivateKey(File inpfil) throws IOException, GeneralSecurityException {
    String[]                            pbeb64s;                                // PBE ASN.1 data base-64 encoded

    byte[]                              pbedta;                                 // PBE ASN.1 data in bytes
    EncryptedPrivateKeyInfo             pbeinf;                                 // PBE key info
    PBEParameterSpec                    pbeprm;                                 // PBE parameters
    Cipher                              pbecph;                                 // PBE decryption cipher

    byte[]                              pk8dta;                                 // PKCS#8 ASN.1 data in bytes
    KeyFactory                          pk8fac=KeyFactory.getInstance("RSA");   // PKCS#8 key factory for decoding private key from ASN.1 data.

    pbeb64s=readDataBlocks(inpfil,"ENCRYPTED PRIVATE KEY");
    if(pbeb64s.length!=1) { throw new GeneralSecurityException("The keystore '"+inpfil+"' contains multiple private keys"); }
    pbedta=base64.decode(pbeb64s[0]);
    log.diagln("  - Read private key data");

    pbeinf=new EncryptedPrivateKeyInfo(pbedta);
    pbeprm=(PBEParameterSpec)pbeinf.getAlgParameters().getParameterSpec(PBEParameterSpec.class);
    pbecph=Cipher.getInstance(pbeinf.getAlgName());
    pbecph.init(Cipher.DECRYPT_MODE,pbeDecryptKey,pbeprm);

    pk8dta=pbecph.doFinal(pbeinf.getEncryptedData());
    log.diagln("  - Private Key: Algorithm= "+pbeinf.getAlgName()+", Iterations: "+pbeprm.getIterationCount()+", Salt: "+Base16.toString(pbeprm.getSalt()));
    pk8dta=patchKeyData(inpfil,pk8dta);
    return pk8fac.generatePrivate(new PKCS8EncodedKeySpec(pk8dta));
    }

BinaryPBEKey

import java.io.*;
import java.security.*;
import java.security.spec.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;

class BinaryPBEKey
extends Object
implements SecretKey
{
private final byte[]                    key;

/**
 * Creates a PBE key from a given binary key.
 *
 * @param key       The key.
 */
BinaryPBEKey(byte[] key) throws InvalidKeySpecException {
    if(key==null) { this.key=new byte[0];         }
    else          { this.key=(byte[])key.clone(); }
    Arrays.fill(key,(byte)0);
    }

public byte[] getEncoded() {
    return (byte[])key.clone();
    }

public String getAlgorithm() {
    return "PBEWithMD5AndDES";
    }

public String getFormat() {
    return "RAW";
    }

/**
 * Calculates a hash code value for the object.
 * Objects that are equal will also have the same hashcode.
 */
public int hashCode() {
    int                             ret=0;

    for(int xa=1; xa<this.key.length; xa++) { ret+=(this.key[xa]*xa); }
    return (ret^=getAlgorithm().toLowerCase().hashCode());
    }

public boolean equals(Object obj) {
    if(obj==this                 ) { return true;  }
    if(obj.getClass()!=getClass()) { return false; }

    BinaryPBEKey                    oth=(BinaryPBEKey)obj;

    if(!(oth.getAlgorithm().equalsIgnoreCase(getAlgorithm()))) {
        return false;
        }

    byte[]  othkey=oth.getEncoded();
    boolean ret   =Arrays.equals(key,othkey);
    Arrays.fill(othkey,(byte)0);
    return ret;
    }

public void destroy() {
    Arrays.fill(this.key,(byte)0);
    }

/**
 * Ensure that the password bytes of this key are zeroed out when there are no more references to it.
 */
protected void finalize() throws Throwable {
    try { destroy(); } finally { super.finalize(); }
    }

Patching PKCS # 8

/**
 * Patch the private key ASN.1 data to conform to PKCS#8.
 * <p>
 * The SSLPlus private key is not properly encoded PKCS#8 - the PKCS#1 RSAPrivateKey should have been wrapped
 * inside an OctetString, thus:
 * <pre>
 * SSLPlus Encoding:
 *        0 30  627: SEQUENCE {
 *        4 02    1:   INTEGER 0
 *        7 30   13:   SEQUENCE {
 *        9 06    9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
 *       20 05    0:     NULL
 *                 :     }
 *       22 30  605:   SEQUENCE {
 *       26 02    1:     INTEGER 0
 *       29 02  129:     INTEGER
 *                 :       00 CA 72 B8 D1 B8 8E B9 39 C0 92 C1 4C 53 B4 F4
 *       ...
 *
 * PKCS#8 Encoding
 *       0 30  631: SEQUENCE {
 *       4 02    1:   INTEGER 0
 *       7 30   13:   SEQUENCE {
 *       9 06    9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
 *      20 05    0:     NULL
 *                :     }
 * ==>  22 04  609:   OCTET STRING, encapsulates {
 *      26 30  605:       SEQUENCE {
 *      30 02    1:         INTEGER 0
 *      33 02  129:         INTEGER
 *                :           00 CA 72 B8 D1 B8 8E B9 39 C0 92 C1 4C 53 B4 F4
 *      ...
 * </pre>
 *
 * Hex Dumps (1K key, space padded for clarity):
 *    Before      : 30 820271 020100300D06092A864886F70D0101010500           30 82025B ... A228
 *    After       : 30 820275 020100300D06092A864886F70D0101010500 04 82025F 30 82025B ... A228
 *                     ^^^^^^                                         ^^^^^^
 *                     Add 4 for later 0482xxxx                       Original total + 4 - 22 (equals the key length of 025B+4)
 */
private byte[] patchKeyData(File inpfil, byte[] asndta) throws IOException, GeneralSecurityException { // except it really doesn't throw an exception
    ByteArrayOutputStream               patdta=new ByteArrayOutputStream();
    int                                 orglen=decodeAsnLength(inpfil,asndta,1);

    patdta.write(asndta,0,1);                                                   // original leader type
    patdta.write(encodeAsnLength(inpfil,(orglen+4)));                           // new total length
    patdta.write(asndta,4,(22-4));                                              // bit between total length an where octet-string wrapper needs to be inserted
    patdta.write(0x04);                                                         // octet-string type
    patdta.write(encodeAsnLength(inpfil,(orglen+4-22)));                        // octet-string length (key data type+key data length+key data)
    patdta.write(asndta,22,asndta.length-22);                                   // private key data
    return patdta.toByteArray();
    }

private int decodeAsnLength(File inpfil, byte[] asndta, int ofs) throws GeneralSecurityException {
    if((asndta[ofs]&0xFF)==0x82) { return (((asndta[ofs+1]&0x000000FF)<< 8)|((asndta[ofs+2]&0x000000FF)));                                                           }
    else                         { throw new GeneralSecurityException("The private key in file '"+inpfil+"' is not supported (ID="+Base16.toString(asndta,0,4)+")"); }
    }

private byte[] encodeAsnLength(File inpfil, int len) throws GeneralSecurityException {
    if(len>=0x0100 && len<=0xFFFF) { return new byte[]{ (byte)0x82,(byte)((len>>>8)&0x000000FF),(byte)len };                                                            }
    else                           { throw new GeneralSecurityException("The new length of "+len+" for patching the private key in file '"+inpfil+"' is out of range"); }
    }
È stato utile?

Soluzione

Ho appena scaricato i tuoi dati decrittografati in un parser ASN.1, e mi sembra perfettamente ASN.1 perfetto per me:

       0 30  627: SEQUENCE {
       4 02    1:   INTEGER 0
       7 30   13:   SEQUENCE {
       9 06    9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
      20 05    0:     NULL
                :     }
      22 30  605:   SEQUENCE {
      26 02    1:     INTEGER 0
      29 02  129:     INTEGER
                :       00 CA 72 B8 D1 B8 8E B9 39 C0 92 C1 4C 53 B4 F4
                :       38 48 3F C3 1C DC 6B BC BE 26 A3 B2 F7 7C 60 A8
                :       2C 0D 86 ED FC 2D D2 5C 99 B6 B6 71 A8 6D 2F 51
                :       25 FA 9C 42 FE 10 C1 2F 39 EA E8 FF 1A 78 BA 6B
                :       64 B8 39 34 3B F4 1C 45 06 C3 B9 98 DC 01 FF 41
                :       56 36 4F DD 35 69 A4 27 BB 5F FD DD 5C 73 BA 9A
                :       94 5A 4F 37 A9 48 3D 5B 89 EA EE BA 8D 02 6E D7
                :       6E D4 6F BC 7D 7A A4 41 4C 4D CA 08 05 20 66 A3
                :               [ Another 1 bytes skipped ]
     161 02    3:     INTEGER 65537
     166 02  128:     INTEGER
                :       21 6A E2 7B 2B DD D3 51 67 2A 52 62 09 07 3B B0
                :       F6 AC 1F C6 E9 D3 96 EA 44 72 8D 1E 31 17 BB 6A
                :       DA 28 C5 AB F4 DC 5E 90 B9 0A 50 A4 9E B1 4A D1
                :       DC 16 63 30 91 0F 72 7E 3A FA 8E F1 8D B0 27 FD
                :       C2 BA B5 F8 FC 7C 46 C0 FD AD A7 39 7C 36 71 7A
                :       33 8B AD 0D 0C DA 50 B7 0E BF D8 64 7D 44 BD 64
                :       6F E2 51 B7 5E 2D 7B BA 02 DB A6 2F 20 88 66 98
                :       85 34 2E EF D4 29 61 23 79 87 27 27 55 15 8D 21
     297 02   65:     INTEGER
                :       00 F9 62 BD 22 4A C8 56 7A C3 17 EB CE CC 5F 42
                :       E1 40 F5 A5 66 60 32 54 86 67 26 AD 7C 34 C2 FE
                :       FE 8A F7 7F BE 79 53 5F C9 73 D9 47 8B 0F 89 A1
                :       09 F1 27 16 FC F1 4B C3 A9 27 59 29 0D DA 9C AE
                :       53
     364 02   65:     INTEGER
                :       00 CF D1 4A 31 50 9A B4 BA 90 42 25 49 54 7C 20
                :       54 2E CF E8 F1 35 DA 92 C2 A3 94 9D B7 B1 85 3F
                :       13 D0 CA BC 77 D9 8A F3 32 83 59 93 E1 F0 11 1B
                :       4C E5 A2 30 50 FE 1F B6 8D A5 B1 44 DA 4D 4B 11
                :       09
     431 02   64:     INTEGER
                :       46 53 3A C4 9D D4 0A D7 09 87 08 5F 43 B0 A5 5A
                :       82 08 03 81 70 25 21 42 D9 79 C5 B8 5D E4 93 25
                :       D2 A8 62 A4 A2 F0 08 F5 F5 2E 53 87 7A 75 34 2D
                :       6A 8C BC 65 CD E1 B0 A6 55 CB 45 D1 7B 51 6D B3
     497 02   65:     INTEGER
                :       00 81 CC 61 7F 9D AD 92 F5 F7 86 28 CD BD 43 ED
                :       D9 46 87 BB 21 75 16 78 95 B3 1F EE C6 3D CD 50
                :       91 6A D6 45 92 C1 C0 24 97 C7 2C 5A CE 42 68 1C
                :       DA 11 8F 14 88 71 C0 92 FF B3 9E 9D B7 8F 91 34
                :       29
     564 02   65:     INTEGER
                :       00 88 7A 99 AC AA A9 D5 2B 6E E1 87 0A E8 D2 4C
                :       04 8E A2 EA 00 3F 8D AF 9F 76 61 86 B0 1D 18 69
                :       C8 64 22 D4 6B A3 A4 BB 52 B1 AC 38 DB 6B 5C 28
                :       F0 78 73 3E 37 FD C8 54 72 C7 FD A9 EB C9 F2 45
                :       96
                :     }
                :   }

Sfortunatamente non è un PrivateKeyInfo codificato correttamente PKCS # 8. La sequenza che parte dall'indice 22 è una PKCS1 # PKCS1RSAPrivateKey, che avrebbe dovuto essere racchiusa in una OctetString per consentire la corretta codifica della struttura.

Prova invece:         30820277020100300D06092A864886F70D0101010500048202613082025D02010002818100CA72B8D1B88EB939C092C14C53B4F438483FC31CDC6BBCBE26A3B2F77C60A82C0D86EDFC2DD25C99B6B671A86D2F5125FA9C42FE10C12F39EAE8FF1A78BA6B64B839343BF41C4506C3B998DC01FF4156364FDD3569A427BB5FFDDD5C73BA9A945A4F37A9483D5B89EAEEBA8D026ED76ED46FBC7D7AA4414C4DCA08052066A3EB0203010001028180216AE27B2BDDD351672A526209073BB0F6AC1FC6E9D396EA44728D1E3117BB6ADA28C5ABF4DC5E90B90A50A49EB14AD1DC166330910F727E3AFA8EF18DB027FDC2BAB5F8FC7C46C0FDADA7397C36717A338BAD0D0CDA50B70EBFD8647D44BD646FE251B75E2D7BBA02DBA62F2088669885342EEFD42961237987272755158D21024100F962BD224AC8567AC317EBCECC5F42E140F5A566603254866726AD7C34C2FEFE8AF77FBE79535FC973D9478B0F89A109F12716FCF14BC3A92759290DDA9CAE53024100CFD14A31509AB4BA90422549547C20542ECFE8F135DA92C2A3949DB7B1853F13D0CABC77D98AF332835993E1F0111B4CE5A23050FE1FB68DA5B144DA4D4B1109024046533AC49DD40AD70987085F43B0A55A8208038170252142D979C5B85DE49325D2A862A4A2F008F5F52E53877A75342D6A8CBC65CDE1B0A655CB45D17B516D B302410081CC617F9DAD92F5F78628CDBD43EDD94687BB2175167895B31FEEC63DCD50916AD64592C1C02497C72C5ACE42681CDA118F148871C092FFB39E9DB78F913429024100887A99ACAAA9D52B6EE1870AE8D24C048EA2EA003F8DAF9F766186B01D1869C86422D46BA3A4BB52B1AC38DB6B5C28F078733E37FDC85472C7FDA9EBC9F24596

Analizzato:

   0 30  631: SEQUENCE {
   4 02    1:   INTEGER 0
   7 30   13:   SEQUENCE {
   9 06    9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
  20 05    0:     NULL
            :     }
  22 04  609:   OCTET STRING, encapsulates {
  26 30  605:       SEQUENCE {
  30 02    1:         INTEGER 0
  33 02  129:         INTEGER
            :           00 CA 72 B8 D1 B8 8E B9 39 C0 92 C1 4C 53 B4 F4
            :           38 48 3F C3 1C DC 6B BC BE 26 A3 B2 F7 7C 60 A8
            :           2C 0D 86 ED FC 2D D2 5C 99 B6 B6 71 A8 6D 2F 51
            :           25 FA 9C 42 FE 10 C1 2F 39 EA E8 FF 1A 78 BA 6B
            :           64 B8 39 34 3B F4 1C 45 06 C3 B9 98 DC 01 FF 41
            :           56 36 4F DD 35 69 A4 27 BB 5F FD DD 5C 73 BA 9A
            :           94 5A 4F 37 A9 48 3D 5B 89 EA EE BA 8D 02 6E D7
            :           6E D4 6F BC 7D 7A A4 41 4C 4D CA 08 05 20 66 A3
            :                   [ Another 1 bytes skipped ]
 165 02    3:         INTEGER 65537
 170 02  128:         INTEGER
            :           21 6A E2 7B 2B DD D3 51 67 2A 52 62 09 07 3B B0
            :           F6 AC 1F C6 E9 D3 96 EA 44 72 8D 1E 31 17 BB 6A
            :           DA 28 C5 AB F4 DC 5E 90 B9 0A 50 A4 9E B1 4A D1
            :           DC 16 63 30 91 0F 72 7E 3A FA 8E F1 8D B0 27 FD
            :           C2 BA B5 F8 FC 7C 46 C0 FD AD A7 39 7C 36 71 7A
            :           33 8B AD 0D 0C DA 50 B7 0E BF D8 64 7D 44 BD 64
            :           6F E2 51 B7 5E 2D 7B BA 02 DB A6 2F 20 88 66 98
            :           85 34 2E EF D4 29 61 23 79 87 27 27 55 15 8D 21
 301 02   65:         INTEGER
            :           00 F9 62 BD 22 4A C8 56 7A C3 17 EB CE CC 5F 42
            :           E1 40 F5 A5 66 60 32 54 86 67 26 AD 7C 34 C2 FE
            :           FE 8A F7 7F BE 79 53 5F C9 73 D9 47 8B 0F 89 A1
            :           09 F1 27 16 FC F1 4B C3 A9 27 59 29 0D DA 9C AE
            :           53
 368 02   65:         INTEGER
            :           00 CF D1 4A 31 50 9A B4 BA 90 42 25 49 54 7C 20
            :           54 2E CF E8 F1 35 DA 92 C2 A3 94 9D B7 B1 85 3F
            :           13 D0 CA BC 77 D9 8A F3 32 83 59 93 E1 F0 11 1B
            :           4C E5 A2 30 50 FE 1F B6 8D A5 B1 44 DA 4D 4B 11
            :           09
 435 02   64:         INTEGER
            :           46 53 3A C4 9D D4 0A D7 09 87 08 5F 43 B0 A5 5A
            :           82 08 03 81 70 25 21 42 D9 79 C5 B8 5D E4 93 25
            :           D2 A8 62 A4 A2 F0 08 F5 F5 2E 53 87 7A 75 34 2D
            :           6A 8C BC 65 CD E1 B0 A6 55 CB 45 D1 7B 51 6D B3
 501 02   65:         INTEGER
            :           00 81 CC 61 7F 9D AD 92 F5 F7 86 28 CD BD 43 ED
            :           D9 46 87 BB 21 75 16 78 95 B3 1F EE C6 3D CD 50
            :           91 6A D6 45 92 C1 C0 24 97 C7 2C 5A CE 42 68 1C
            :           DA 11 8F 14 88 71 C0 92 FF B3 9E 9D B7 8F 91 34
            :           29
 568 02   65:         INTEGER
            :           00 88 7A 99 AC AA A9 D5 2B 6E E1 87 0A E8 D2 4C
            :           04 8E A2 EA 00 3F 8D AF 9F 76 61 86 B0 1D 18 69
            :           C8 64 22 D4 6B A3 A4 BB 52 B1 AC 38 DB 6B 5C 28
            :           F0 78 73 3E 37 FD C8 54 72 C7 FD A9 EB C9 F2 45
            :           96
            :         }
            :       }
            :   }

Per correggere i tuoi file, puoi usare una libreria ASN.1 (ma non ne sono a conoscenza di una buona per Java), oppure fare quanto segue:

Verifica che i tuoi dati inizino con     30(*1)020100300D06092A864886F70D010101050030(*2) (*1) e (*2) saranno codifiche di lunghezza in una delle seguenti forme

  • lunghezza < = 0x7F: XX, dove XX è la lunghezza
  • 0x80 < = lunghezza < = 0xFF: 81XX, dove XX è la lunghezza
  • 0x0100 < = lunghezza < = 0xFFFF: 82XXXX, dove XXXX è la lunghezza
  • 0x010000 < = lunghezza < = 0xFFFFFF: 83XXXXXX, dove XXXXXX è la lunghezza ecc.

Se le tue chiavi hanno tutte la stessa lunghezza, probabilmente puoi presumere che le codifiche di lunghezza siano sempre nel modulo 30(*2), ma le lunghezze effettive probabilmente varieranno.

Leggi la lunghezza in (*3), aggiungi la lunghezza in byte di 04(*3) al numero (probabilmente è 4) e codifica la lunghezza come sopra (molto probabilmente sarà il modulo <=>). Chiamiamo questa codifica di lunghezza <=>. Inserisci <=> subito prima di <=>. Ora aggiungi la lunghezza di <=> (probabilmente anche 4) a <=> e ricodifica questo (probabilmente può ancora adattarsi in <=>) e sostituisci <=> con questo.

Spero fosse comprensibile, altrimenti consiglio di leggere Una guida di Layman a un sottoinsieme di ASN.1, BER e DER .

Altri suggerimenti

Hai provato ad aprire la chiave con alcune delle classi interne di Bouncy Castle? Forse usandoli direttamente invece di definire BC come provider di crittografia puoi analizzare quel file ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top